{
    "file_diffs": [
        {
            "old_file_content": "# pylint: disable=missing-docstring\nfrom typing import Iterable, Set\nfrom collections import defaultdict\nfrom itertools import product, chain\n\nimport numpy as np\nfrom scipy import sparse as sp\n\nfrom AnyQt.QtCore import (Qt, QSize, QItemSelection, QItemSelectionModel,\n                          pyqtSignal)\nfrom AnyQt.QtGui import QStandardItem, QColor, QStandardItemModel\nfrom AnyQt.QtWidgets import (QTableView, QSizePolicy, QHeaderView,\n                             QStyledItemDelegate, QCheckBox, QFrame, QWidget,\n                             QGridLayout)\n\nfrom Orange.data import (Table, DiscreteVariable, Variable, Domain,\n                         ContinuousVariable, TimeVariable)\nfrom Orange.data.domain import filter_visible\nfrom Orange.data.util import get_unique_names_duplicates, get_unique_names\nfrom Orange.data.filter import FilterContinuous, FilterDiscrete, Values\nfrom Orange.statistics.util import (nanmin, nanmax, nanunique, nansum, nanvar,\n                                    nanmean, nanmedian, nanmode, bincount)\nfrom Orange.util import Enum\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import (Setting, ContextSetting,\n                                     DomainContextHandler)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Input, Output, Msg\n\n\nBorderRole = next(gui.OrangeUserRole)\nBorderColorRole = next(gui.OrangeUserRole)\n\n\nclass AggregationFunctionsEnum(Enum):\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = range(10)\n\n    def __init__(self, *_, **__):\n        super().__init__()\n        self.func = None\n\n    @property\n    def value(self):\n        return self._value_\n\n    def __call__(self, *args):\n        return self.func(args)  # pylint: disable=not-callable\n\n    def __str__(self):\n        return self._name_.replace(\"_\", \" \")\n\n    def __gt__(self, other):\n        return self._value_ > other.value\n\n\nclass Pivot:\n    Functions = AggregationFunctionsEnum\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = Functions\n\n    AutonomousFunctions = (Count,)\n    AnyVarFunctions = (Count_defined,)\n    ContVarFunctions = (Sum, Mean, Min, Max, Mode, Median, Var)\n    DiscVarFunctions = (Majority,)\n    TimeVarFunctions = (Mean, Min, Max, Mode, Median)\n    FloatFunctions = (Count, Count_defined, Sum, Var)\n\n    class Tables:\n        table = None  # type: Table\n        total_h = None  # type: Table\n        total_v = None  # type: Table\n        total = None  # type: Table\n\n        def __call__(self):\n            return self.table, self.total_h, self.total_v, self.total\n\n    def __init__(self, table: Table, agg_funs: Iterable[Functions],\n                 row_var: Variable, col_var: Variable = None,\n                 val_var: Variable = None):\n        self._group_tables = self.Tables()\n        self._pivot_tables = self.Tables()\n        self._table = table\n        self._row_var = row_var\n        self._col_var = col_var if col_var else row_var\n        self.renamed = []\n\n        if not table:\n            return\n        if not self._row_var.is_primitive():\n            raise TypeError(\"Row variable should be DiscreteVariable\"\n                            \" or ContinuousVariable\")\n        if self._col_var and not self._col_var.is_discrete:\n            raise TypeError(\"Column variable should be DiscreteVariable\")\n\n        self._row_var_col = table.get_column_view(row_var)[0].astype(np.float)\n        self._col_var_col = table.get_column_view(self._col_var)[0].astype(np.float)\n        self._row_var_groups = nanunique(self._row_var_col)\n        self._col_var_groups = nanunique(self._col_var_col)\n\n        self._total_var = DiscreteVariable(\"Total\", values=(\"total\", ))\n        self._current_agg_functions = sorted(agg_funs)\n        self._indepen_agg_done = {}  # type: Dict[Functions, int]\n        self._depen_agg_done = {}  # type: Dict[Functions, Dict[Variable, int]]\n\n        self._initialize(agg_funs, val_var)\n\n    @property\n    def group_table(self) -> Table:\n        table = self._group_tables.table\n        if not table or len(table) == 0:\n            return None\n        indices = [0, 1] if not self.single_var_grouping else [0]\n        for f in self._current_agg_functions:\n            if f in self._indepen_agg_done:\n                indices.append(self._indepen_agg_done[f])\n        for v in self._table.domain.variables + self._table.domain.metas:\n            for f in self._current_agg_functions:\n                if f in self._depen_agg_done and v in self._depen_agg_done[f]:\n                    indices.append(self._depen_agg_done[f][v])\n        return table[:, indices]\n\n    @property\n    def pivot_table(self) -> Table:\n        return self._pivot_tables.table\n\n    @property\n    def pivot_total_h(self) -> Table:\n        return self._pivot_tables.total_h\n\n    @property\n    def pivot_total_v(self) -> Table:\n        return self._pivot_tables.total_v\n\n    @property\n    def pivot_total(self) -> Table:\n        return self._pivot_tables.total\n\n    @property\n    def pivot_tables(self) -> Table:\n        return self._pivot_tables()\n\n    @property\n    def single_var_grouping(self) -> bool:\n        return self._row_var is self._col_var\n\n    def update_group_table(self, agg_funs: Iterable[Functions],\n                           val_var: Variable = None):\n        if not self._group_tables:\n            return\n        self._current_agg_functions = sorted(agg_funs)\n        agg_funs = set(self._indepen_agg_done.keys()) | \\\n            set(self._depen_agg_done.keys()) | set(agg_funs)\n        self._initialize(sorted(agg_funs), val_var)\n\n    def _initialize(self, agg_funs, val_var):\n        var_indep_funs, var_dep_funs = self.__group_aggregations(agg_funs)\n        self._create_group_tables(var_indep_funs, var_dep_funs)\n        self.__reference_aggregations(var_indep_funs, var_dep_funs)\n        self._create_pivot_tables(val_var)\n\n    def __group_aggregations(self, agg_funs):\n        auto_funcs = self.AutonomousFunctions\n        var_indep_funs = [fun for fun in agg_funs if fun in auto_funcs]\n        var_dep_funs = []\n        attrs = self._table.domain.variables + self._table.domain.metas\n        prod = product(filter_visible(attrs),\n                       [fun for fun in agg_funs if fun not in auto_funcs])\n        for var, fun in prod:\n            if self.__include_aggregation(fun, var):\n                var_dep_funs.append((var, fun))\n        return var_indep_funs, var_dep_funs\n\n    def __include_aggregation(self, fun, var):\n        return fun in self.ContVarFunctions and var.is_continuous or \\\n               fun in self.DiscVarFunctions and var.is_discrete or \\\n               fun in self.AnyVarFunctions\n\n    def __reference_aggregations(self, var_indep_funs, var_dep_funs):\n        self._indepen_agg_done = {}\n        self._depen_agg_done = defaultdict(dict)\n        i = 1 - int(bool(self.single_var_grouping))\n        for i, fun in enumerate(var_indep_funs, i + 1):\n            self._indepen_agg_done[fun] = i\n        for j, (var, fun) in enumerate(var_dep_funs, i + 1):\n            self._depen_agg_done[fun].update({var: j})\n\n    def _create_group_tables(self, var_indep_funs, var_dep_funs):\n        attrs = [ContinuousVariable(f\"({str(fun).lower()})\")\n                 for fun in var_indep_funs]\n        for var, fun in var_dep_funs:\n            name = f\"{var.name} ({str(fun).lower()})\"\n            if fun in self.DiscVarFunctions:\n                attrs.append(DiscreteVariable(name, var.values))\n            else:\n                if isinstance(var, TimeVariable) and \\\n                        fun in self.TimeVarFunctions:\n                    attrs.append(TimeVariable(name, have_date=var.have_date,\n                                              have_time=var.have_time))\n                else:\n                    attrs.append(ContinuousVariable(name))\n        args = (var_indep_funs, var_dep_funs, attrs)\n        for t, var in ((\"table\", None), (\"total_h\", self._col_var),\n                       (\"total_v\", self._row_var), (\"total\", self._total_var)):\n            setattr(self._group_tables, t, self.__get_group_table(var, *args))\n\n    def __get_group_table(self, var, var_indep_funs, var_dep_funs, attrs):\n        if var is self._total_var:\n            group_tab = self._group_tables.total\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._total_var]\n            combs = np.array([[0]])\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (~np.isnan(self._col_var_col)))[0]]\n        elif var is self._row_var or self.single_var_grouping:\n            group_tab = self._group_tables.total_v\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._row_var]\n            combs = self._row_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._col_var_col)) &\n                                     (self._row_var_col == x[0]))[0]]\n        elif var is self._col_var:\n            group_tab = self._group_tables.total_h\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._col_var]\n            combs = self._col_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (self._col_var_col == x[0]))[0]]\n        else:\n            group_tab = self._group_tables.table\n            offset = 0\n            leading_vars = [self._row_var, self._col_var]\n            combs = np.array(list(product(self._row_var_groups,\n                                          self._col_var_groups)))\n            sub_table_getter = lambda x: \\\n                self._table[np.where((self._row_var_col == x[0])\n                                     & (self._col_var_col == x[1]))[0]]\n\n        if not combs.shape[0]:\n            return None\n\n        n = len(var_indep_funs) + len(var_dep_funs)\n        X = np.zeros((len(combs), n), dtype=float)\n        for i, comb in enumerate(combs):\n            sub_table = sub_table_getter(comb)\n            j = -1\n            for j, fun in enumerate(var_indep_funs):\n                if fun in self._indepen_agg_done:\n                    # TODO - optimize - after this line is executed,\n                    # the whole column is already set\n                    X[:, j] = group_tab.X[:, self._indepen_agg_done[fun] - offset]\n                else:\n                    X[i, j] = fun(sub_table)\n            for k, (v, fun) in enumerate(var_dep_funs, j + 1):\n                if fun in self._depen_agg_done:\n                    X[:, k] = group_tab.X[:, self._depen_agg_done[fun][v] - offset]\n                else:\n                    X[i, k] = fun(sub_table.get_column_view(v)[0])\n\n        #rename leading vars (seems the easiest) if needed\n        current = [var.name for var in attrs]\n        uniq_leading_vars = []\n        for v in leading_vars:\n            uniq = get_unique_names(current, v.name)\n            if uniq != v.name:\n                self.renamed.append(v.name)\n                v = v.copy(name=uniq)\n            uniq_leading_vars.append(v)\n            current.append(uniq)\n\n        return Table(Domain(uniq_leading_vars + attrs), np.hstack((combs, X)))\n\n    def update_pivot_table(self, val_var: Variable):\n        self._create_pivot_tables(val_var)\n\n    def _create_pivot_tables(self, val_var):\n        if not self._group_tables.table:\n            self._pivot_tables = self.Tables()\n            return\n\n        agg_funs = [fun for fun in self._current_agg_functions\n                    if fun in self.AutonomousFunctions\n                    or val_var and self.__include_aggregation(fun, val_var)]\n        X, X_h, X_v, X_t = self.__get_pivot_tab_x(val_var, agg_funs)\n        dom, dom_h, dom_v, dom_t = self.__get_pivot_tab_domain(\n            val_var, X, X_h, X_v, X_t, agg_funs)\n        for t, d, x in ((\"table\", dom, X), (\"total_h\", dom_h, X_h),\n                        (\"total_v\", dom_v, X_v), (\"total\", dom_t, X_t)):\n            setattr(self._pivot_tables, t, Table(d, x))\n\n    # pylint: disable=invalid-name\n    def __get_pivot_tab_domain(self, val_var, X, X_h, X_v, X_t, agg_funs):\n        def map_values(index, _X):\n            values = np.unique(_X[:, index])\n            values = np.delete(values, np.where(values == \"nan\")[0])\n            for j, value in enumerate(values):\n                _X[:, index][_X[:, index] == value] = j\n            return values\n\n        create_time_var = \\\n            isinstance(val_var, TimeVariable) and \\\n            all(fun in self.TimeVarFunctions for fun in agg_funs)\n        create_cont_var = \\\n            not val_var or val_var.is_continuous and \\\n            (not isinstance(val_var, TimeVariable) or\n             all(fun in self.FloatFunctions for fun in agg_funs))\n\n        vals = np.array(self._col_var.values)[self._col_var_groups.astype(int)]\n        if create_time_var:\n            kwargs = {\"have_date\": val_var.have_date,\n                      \"have_time\": val_var.have_time}\n            attrs = [[TimeVariable(f\"{v}\", **kwargs) for v in vals]] * 2\n            attrs.extend([[TimeVariable(\"Total\", **kwargs)]] * 2)\n        elif create_cont_var:\n            attrs = [[ContinuousVariable(f\"{v}\", 1) for v in vals]] * 2\n            attrs.extend([[ContinuousVariable(\"Total\", 1)]] * 2)\n        else:\n            attrs = []\n            for x in (X, X_h):\n                attrs.append([DiscreteVariable(f\"{v}\", map_values(i, x))\n                              for i, v in enumerate(vals, 2)])\n            for x in (X_v, X_t):\n                attrs.append([DiscreteVariable(\"Total\", map_values(0, x))])\n        row_var_h = DiscreteVariable(self._row_var.name, values=[\"Total\"])\n        aggr_attr = DiscreteVariable('Aggregate', [str(f) for f in agg_funs])\n\n        same_row_col = self._col_var is self._row_var\n\n        extra_vars = [self._row_var, aggr_attr]\n        uniq_a = get_unique_names_duplicates([v.name for v in extra_vars]\n                                             + [atr.name for atr in attrs[0]])\n        for (idx, var), u in zip(enumerate(chain(extra_vars, attrs[0])), uniq_a):\n            if var.name == u:\n                continue\n            if idx == 0:\n                self.renamed.append(self._row_var.name)\n                self._row_var = self._row_var.copy(name=u)\n                if same_row_col:\n                    self._col_var = self._row_var\n                row_var_h = row_var_h.copy(name=u)\n            elif idx == 1:\n                self.renamed.append(aggr_attr.name)\n                aggr_attr = aggr_attr.copy(name=u)\n            else:\n                self.renamed.append(var.name)\n                attrs[0][idx-2] = var.copy(name=u)\n                attrs[1][idx-2] = var.copy(name=u)\n\n        if same_row_col:\n            vals = tuple(v.name for v in attrs[0])\n            self._row_var.make(self._row_var.name, values=vals)\n            vals = tuple(v.name for v in attrs[2])\n            row_var_h.make(row_var_h.name, vals)\n\n        return (Domain([self._row_var, aggr_attr] + attrs[0]),\n                Domain([row_var_h, aggr_attr] + attrs[1]),\n                Domain(attrs[2]), Domain(attrs[3]))\n\n    def __get_pivot_tab_x(self, val_var, agg_funs):\n        gt = self._group_tables\n        n_fun = len(agg_funs)\n        n_rows, n_cols = len(self._row_var_groups), len(self._col_var_groups)\n        is_float_type = not val_var or val_var.is_continuous\n        if isinstance(val_var, TimeVariable):\n            is_float_type = \\\n                all(fun in self.TimeVarFunctions for fun in agg_funs) or \\\n                all(fun in self.FloatFunctions for fun in agg_funs)\n        kwargs = {\"fill_value\": np.nan, \"dtype\": float} if is_float_type \\\n            else {\"fill_value\": \"\", \"dtype\": object}\n        X = np.full((n_rows * n_fun, 2 + n_cols), **kwargs)\n        X_h = np.full((n_fun, 2 + n_cols), **kwargs)\n        X_v = np.full((n_rows * n_fun, 1), **kwargs)\n        X_t = np.full((n_fun, 1), **kwargs)\n        for i, fun in enumerate(agg_funs):\n            args = (val_var, fun, is_float_type)\n            X[i::n_fun, 2:] = self.__rows_for_function(n_rows, n_cols, *args)\n            X[i::n_fun, :2] = np.array([[row_val, agg_funs.index(fun)] for\n                                        row_val in self._row_var_groups])\n            X_h[i, :2] = 0, agg_funs.index(fun)\n            X_h[i, 2:] = self.__total_for_function(gt.total_h, *args)\n            X_v[i::n_fun, 0] = self.__total_for_function(gt.total_v, *args)\n            X_t[i] = self.__total_for_function(gt.total, *args)\n        return X, X_h, X_v, X_t\n\n    def __total_for_function(self, group_tab, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        ref -= int(bool(not self.single_var_grouping))\n        return self.__check_continuous(val_var, group_tab.X[:, ref],\n                                       fun, is_float_type)\n\n    def __rows_for_function(self, n_rows, n_cols, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        column = self._group_tables.table.X[:, ref]\n        if self.single_var_grouping:\n            rows = np.full((n_rows, n_cols), fun(np.array([]), ), dtype=float)\n            rows[np.diag_indices_from(rows)] = column\n        else:\n            rows = column.reshape(n_rows, n_cols)\n        return self.__check_continuous(val_var, rows, fun, is_float_type)\n\n    def __check_continuous(self, val_var, column, fun, is_float_type):\n        if val_var and not val_var.is_continuous:\n            column = column.astype(str)\n            if fun in self.DiscVarFunctions:\n                for j, val in enumerate(val_var.values):\n                    column[column == str(float(j))] = val\n        elif isinstance(val_var, TimeVariable) and not is_float_type:\n            shape = column.shape\n            column = column.flatten()\n            column_ = column.astype(str)\n            if fun in self.TimeVarFunctions:\n                for i in range(column.shape[0]):\n                    if not np.isnan(column[i]):\n                        column_[i] = val_var.repr_val(column[i])\n            return column_.reshape(shape)\n        return column\n\n    @staticmethod\n    def count_defined(x):\n        if x.shape[0] == 0:\n            return 0\n        if x.size and np.issubdtype(x.dtype, np.number) and not sp.issparse(x):\n            nans = np.isnan(x).sum(axis=0)\n        elif sp.issparse(x) and x.size:\n            nans = np.bincount(x.nonzero()[1], minlength=x.shape[1])\n            x = x.tocsc()\n        else:\n            x_str = x.astype(str)\n            nans = ((x_str == \"nan\") | (x_str == \"\")).sum(axis=0) \\\n                if x.size else np.zeros(x.shape[1])\n        return x.shape[0] - nans\n\n    @staticmethod\n    def stat(x, f):\n        return f(x.astype(np.float), axis=0) if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def mode(x):\n        return Pivot.stat(x, nanmode).mode if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def majority(x):\n        if x.shape[0] == 0:\n            return np.nan\n        counts = bincount(x)[0]\n        return np.argmax(counts) if counts.shape[0] else np.nan\n\n    Count.func = lambda x: len(x[0])\n    Count_defined.func = lambda x: Pivot.count_defined(x[0])\n    Sum.func = lambda x: nansum(x[0], axis=0) if x[0].shape[0] > 0 else 0\n    Mean.func = lambda x: Pivot.stat(x[0], nanmean)\n    Min.func = lambda x: Pivot.stat(x[0], nanmin)\n    Max.func = lambda x: Pivot.stat(x[0], nanmax)\n    Median.func = lambda x: Pivot.stat(x[0], nanmedian)\n    Mode.func = lambda x: Pivot.mode(x[0])\n    Var.func = lambda x: Pivot.stat(x[0], nanvar)\n    Majority.func = lambda x: Pivot.majority(x[0])\n\n\nclass BorderedItemDelegate(QStyledItemDelegate):\n    def paint(self, painter, option, index):\n        \"\"\"Overloads `paint` to draw borders\"\"\"\n        QStyledItemDelegate.paint(self, painter, option, index)\n        if index.data(BorderRole):\n            painter.save()\n            painter.setPen(index.data(BorderColorRole))\n            rect = option.rect\n            painter.drawLine(rect.topLeft(), rect.topRight())\n            painter.restore()\n\n\nclass PivotTableView(QTableView):\n    selection_changed = pyqtSignal()\n\n    TOTAL_STRING = \"Total\"\n\n    def __init__(self):\n        super().__init__(editTriggers=QTableView.NoEditTriggers)\n        self._n_classesv = None  # number of row_feature values\n        self._n_classesh = None  # number of col_feature values\n        self._n_agg_func = None  # number of aggregation functions\n        self._n_leading_rows = None  # number of leading rows\n        self._n_leading_cols = None  # number of leading columns\n\n        self.table_model = QStandardItemModel(self)\n        self.setModel(self.table_model)\n        self.horizontalHeader().hide()\n        self.verticalHeader().hide()\n        self.horizontalHeader().setMinimumSectionSize(60)\n        self.setShowGrid(False)\n        self.setSizePolicy(QSizePolicy.MinimumExpanding,\n                           QSizePolicy.MinimumExpanding)\n        self.setItemDelegate(BorderedItemDelegate())\n        self.pressed.connect(self.__cell_clicked)\n        self.clicked.connect(self.__cell_clicked)\n        self.entered.connect(self.__cell_entered)\n        self.__clicked_cell = None\n\n    @property\n    def add_agg_column(self) -> bool:\n        return self._n_agg_func > 1\n\n    def __cell_entered(self, model_index):\n        if self.__clicked_cell is None:\n            return\n        index = self.table_model.index\n        selection = None\n        i_end, j_end = model_index.row(), model_index.column()\n        i_start, j_start = self.__clicked_cell\n        i_start, i_end = sorted([i_start, i_end])\n        j_start, j_end = sorted([j_start, j_end])\n        if i_start >= self._n_leading_rows and j_start >= self._n_leading_cols:\n            i_start = (i_start - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows\n            i_end = (i_end - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows + self._n_agg_func - 1\n            start, end = index(i_start, j_start), index(i_end, j_end)\n            selection = QItemSelection(start, end)\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n        self.selection_changed.emit()\n\n    def __cell_clicked(self, model_index):\n        i, j = model_index.row(), model_index.column()\n        self.__clicked_cell = (i, j)\n        m, n = self.table_model.rowCount(), self.table_model.columnCount()\n        index = self.table_model.index\n        selection = None\n        if i > m - self._n_agg_func - 1 and j == n - 1:\n            start_index = index(self._n_leading_rows, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(m - 1, n - 1))\n        elif i == self._n_leading_rows - 1 or i > m - self._n_agg_func - 1:\n            start_index = index(self._n_leading_rows, j)\n            selection = QItemSelection(start_index, index(m - 1, j))\n        elif j in (self._n_leading_cols - 1, n - 1, 1):\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            start_index = index(i_start, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(i_end, n - 1))\n        elif i >= self._n_leading_rows and j >= self._n_leading_cols:\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            selection = QItemSelection(index(i_start, j), index(i_end, j))\n\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n\n    def mouseReleaseEvent(self, e):\n        super().mouseReleaseEvent(e)\n        self.selection_changed.emit()\n\n    def update_table(self, titleh: str, titlev: str, table: Table,\n                     table_total_h: Table, table_total_v: Table,\n                     table_total: Table):\n        self.clear()\n        if not table:\n            return\n\n        self._initialize(table, table_total_h)\n        self._set_headers(titleh, titlev, table)\n        self._set_values(table[:, 2:])\n        self._set_totals(table_total_h[:, 2:], table_total_v, table_total)\n        self._draw_lines()\n        self._resize(table)\n\n    def _initialize(self, table, table_total_h):\n        self._n_classesv = int(len(table) / len(table_total_h))\n        self._n_classesh = table.X.shape[1] - 2\n        self._n_agg_func = len(table_total_h)\n        self._n_leading_rows = 2\n        self._n_leading_cols = 2 + int(len(table_total_h) > 1)\n\n    def _set_headers(self, titleh, titlev, table):\n        self.__set_horizontal_title(titleh)\n        self.__set_vertical_title(titlev)\n        self.__set_flags_title()\n        self.__set_horizontal_headers(table)\n        self.__set_vertical_headers(table)\n\n    def __set_horizontal_title(self, titleh):\n        item = QStandardItem()\n        item.setData(titleh, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignCenter)\n        self.table_model.setItem(0, self._n_leading_cols, item)\n        self.setSpan(0, self._n_leading_cols, 1, self._n_classesh + 3)\n\n    def __set_vertical_title(self, titlev):\n        item = QStandardItem()\n        item.setData(titlev, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)\n        self.setItemDelegateForColumn(0, gui.VerticalItemDelegate(extend=True))\n        self.table_model.setItem(self._n_leading_rows, 0, item)\n        row_span = self._n_classesv * self._n_agg_func + 1\n        self.setSpan(self._n_leading_rows, 0, row_span, 1)\n\n    def __set_flags_title(self):\n        item = self.table_model.item(0, self._n_leading_cols)\n        item.setFlags(Qt.NoItemFlags)\n        item = self.table_model.item(self._n_leading_rows, 0)\n        item.setFlags(Qt.NoItemFlags)\n        for i, j in product(range(self._n_leading_rows),\n                            range(self._n_leading_cols)):\n            item = QStandardItem()\n            item.setFlags(Qt.NoItemFlags)\n            self.table_model.setItem(i, j, item)\n\n    def __set_horizontal_headers(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if not self.add_agg_column:\n            labels[0] = str(table[0, 1])\n        for i, label in enumerate(labels, self._n_leading_cols - 1):\n            self.table_model.setItem(1, i, self._create_header_item(label))\n\n    def __set_vertical_headers(self, table):\n        labels = [(str(row[0]), str(row[1])) for row in table]\n        i = self._n_leading_rows - 1\n        for i, (l1, l2) in enumerate(labels, self._n_leading_rows):\n            l1 = \"\" if (i - self._n_leading_rows) % self._n_agg_func else l1\n            self.table_model.setItem(i, 1, self._create_header_item(l1))\n            if self.add_agg_column:\n                self.table_model.setItem(i, 2, self._create_header_item(l2))\n\n        if self.add_agg_column:\n            labels = [str(row[1]) for row in table[:self._n_agg_func]]\n            start = self._n_leading_rows + self._n_agg_func * self._n_classesv\n            for j, l2 in enumerate(labels, i + 1):\n                l1 = self.TOTAL_STRING if j == start else \"\"\n                self.table_model.setItem(j, 1, self._create_header_item(l1))\n                self.table_model.setItem(j, 2, self._create_header_item(l2))\n        else:\n            item = self._create_header_item(self.TOTAL_STRING)\n            self.table_model.setItem(i + 1, 1, item)\n\n    def _set_values(self, table):\n        for i, j in product(range(len(table)), range(len(table[0]))):\n            value = table[i, j]\n            item = self._create_value_item(str(value))\n            self.table_model.setItem(i + self._n_leading_rows,\n                                     j + self._n_leading_cols, item)\n\n    def _set_totals(self, table_total_h, table_total_v, table_total):\n        def set_total_item(table, get_row, get_col):\n            for i, j in product(range(len(table)), range(len(table[0]))):\n                item = self._create_header_item(str(table[i, j]))\n                self.table_model.setItem(get_row(i), get_col(j), item)\n\n        last_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        last_col = self._n_leading_cols + self._n_classesh\n        set_total_item(table_total_v, lambda x: x + self._n_leading_rows,\n                       lambda x: last_col)\n        set_total_item(table_total_h, lambda x: x + last_row,\n                       lambda x: x + self._n_leading_cols)\n        set_total_item(table_total, lambda x: x + last_row, lambda x: last_col)\n\n    def _create_header_item(self, text):\n        bold_font = self.table_model.invisibleRootItem().font()\n        bold_font.setBold(True)\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setFont(bold_font)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled)\n        return item\n\n    @staticmethod\n    def _create_value_item(text):\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)\n        return item\n\n    def _draw_lines(self):\n        end_col = self._n_leading_cols + self._n_classesh + 1\n        total_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        indices = [(total_row, j) for j in range(1, end_col)]\n        for i in range(self._n_classesv):\n            inner_row = self._n_agg_func * i + self._n_leading_rows\n            inner_indices = [(inner_row, j) for j in range(1, end_col)]\n            indices = indices + inner_indices\n            if not self.add_agg_column:\n                break\n        for i, j in indices:\n            item = self.table_model.item(i, j)\n            item.setData(\"t\", BorderRole)\n            item.setData(QColor(160, 160, 160), BorderColorRole)\n\n    def _resize(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if len(' '.join(labels)) < 120:\n            self.horizontalHeader().setSectionResizeMode(\n                QHeaderView.ResizeToContents)\n        else:\n            self.horizontalHeader().setDefaultSectionSize(60)\n\n    def get_selection(self) -> Set:\n        m, n = self._n_leading_rows, self._n_leading_cols\n        return {(ind.row() - m, ind.column() - n)\n                for ind in self.selectedIndexes()}\n\n    def set_selection(self, indexes: Set):\n        selection = QItemSelection()\n        index = self.model().index\n        for row, col in indexes:\n            sel = index(row + self._n_leading_rows, col + self._n_leading_cols)\n            selection.select(sel, sel)\n        self.selectionModel().select(\n            selection, QItemSelectionModel.ClearAndSelect)\n\n    def clear(self):\n        self.table_model.clear()\n\n\nclass OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n                if domain.variables[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)\n\n\nif __name__ == \"__main__\":\n    WidgetPreview(OWPivot).run(set_data=Table(\"heart_disease\"))\n",
            "new_file_content": "# pylint: disable=missing-docstring\nfrom typing import Iterable, Set\nfrom collections import defaultdict\nfrom itertools import product, chain\n\nimport numpy as np\nfrom scipy import sparse as sp\n\nfrom AnyQt.QtCore import (Qt, QSize, QItemSelection, QItemSelectionModel,\n                          pyqtSignal)\nfrom AnyQt.QtGui import QStandardItem, QColor, QStandardItemModel\nfrom AnyQt.QtWidgets import (QTableView, QSizePolicy, QHeaderView,\n                             QStyledItemDelegate, QCheckBox, QFrame, QWidget,\n                             QGridLayout)\n\nfrom Orange.data import (Table, DiscreteVariable, Variable, Domain,\n                         ContinuousVariable, TimeVariable)\nfrom Orange.data.domain import filter_visible\nfrom Orange.data.util import get_unique_names_duplicates, get_unique_names\nfrom Orange.data.filter import FilterContinuous, FilterDiscrete, Values\nfrom Orange.statistics.util import (nanmin, nanmax, nanunique, nansum, nanvar,\n                                    nanmean, nanmedian, nanmode, bincount)\nfrom Orange.util import Enum\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import (Setting, ContextSetting,\n                                     DomainContextHandler)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Input, Output, Msg\n\n\nBorderRole = next(gui.OrangeUserRole)\nBorderColorRole = next(gui.OrangeUserRole)\n\n\nclass AggregationFunctionsEnum(Enum):\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = range(10)\n\n    def __init__(self, *_, **__):\n        super().__init__()\n        self.func = None\n\n    @property\n    def value(self):\n        return self._value_\n\n    def __call__(self, *args):\n        return self.func(args)  # pylint: disable=not-callable\n\n    def __str__(self):\n        return self._name_.replace(\"_\", \" \")\n\n    def __gt__(self, other):\n        return self._value_ > other.value\n\n\nclass Pivot:\n    Functions = AggregationFunctionsEnum\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = Functions\n\n    AutonomousFunctions = (Count,)\n    AnyVarFunctions = (Count_defined,)\n    ContVarFunctions = (Sum, Mean, Min, Max, Mode, Median, Var)\n    DiscVarFunctions = (Majority,)\n    TimeVarFunctions = (Mean, Min, Max, Mode, Median)\n    FloatFunctions = (Count, Count_defined, Sum, Var)\n\n    class Tables:\n        table = None  # type: Table\n        total_h = None  # type: Table\n        total_v = None  # type: Table\n        total = None  # type: Table\n\n        def __call__(self):\n            return self.table, self.total_h, self.total_v, self.total\n\n    def __init__(self, table: Table, agg_funs: Iterable[Functions],\n                 row_var: Variable, col_var: Variable = None,\n                 val_var: Variable = None):\n        self._group_tables = self.Tables()\n        self._pivot_tables = self.Tables()\n        self._table = table\n        self._row_var = row_var\n        self._col_var = col_var if col_var else row_var\n        self.renamed = []\n\n        if not table:\n            return\n        if not self._row_var.is_primitive():\n            raise TypeError(\"Row variable should be DiscreteVariable\"\n                            \" or ContinuousVariable\")\n        if self._col_var and not self._col_var.is_discrete:\n            raise TypeError(\"Column variable should be DiscreteVariable\")\n\n        self._row_var_col = table.get_column_view(row_var)[0].astype(np.float)\n        self._col_var_col = table.get_column_view(self._col_var)[0].astype(np.float)\n        self._row_var_groups = nanunique(self._row_var_col)\n        self._col_var_groups = nanunique(self._col_var_col)\n\n        self._total_var = DiscreteVariable(\"Total\", values=(\"total\", ))\n        self._current_agg_functions = sorted(agg_funs)\n        self._indepen_agg_done = {}  # type: Dict[Functions, int]\n        self._depen_agg_done = {}  # type: Dict[Functions, Dict[Variable, int]]\n\n        self._initialize(agg_funs, val_var)\n\n    @property\n    def group_table(self) -> Table:\n        table = self._group_tables.table\n        if not table or len(table) == 0:\n            return None\n        indices = [0, 1] if not self.single_var_grouping else [0]\n        for f in self._current_agg_functions:\n            if f in self._indepen_agg_done:\n                indices.append(self._indepen_agg_done[f])\n        for v in self._table.domain.variables + self._table.domain.metas:\n            for f in self._current_agg_functions:\n                if f in self._depen_agg_done and v in self._depen_agg_done[f]:\n                    indices.append(self._depen_agg_done[f][v])\n        return table[:, indices]\n\n    @property\n    def pivot_table(self) -> Table:\n        return self._pivot_tables.table\n\n    @property\n    def pivot_total_h(self) -> Table:\n        return self._pivot_tables.total_h\n\n    @property\n    def pivot_total_v(self) -> Table:\n        return self._pivot_tables.total_v\n\n    @property\n    def pivot_total(self) -> Table:\n        return self._pivot_tables.total\n\n    @property\n    def pivot_tables(self) -> Table:\n        return self._pivot_tables()\n\n    @property\n    def single_var_grouping(self) -> bool:\n        return self._row_var is self._col_var\n\n    def update_group_table(self, agg_funs: Iterable[Functions],\n                           val_var: Variable = None):\n        if not self._group_tables:\n            return\n        self._current_agg_functions = sorted(agg_funs)\n        agg_funs = set(self._indepen_agg_done.keys()) | \\\n            set(self._depen_agg_done.keys()) | set(agg_funs)\n        self._initialize(sorted(agg_funs), val_var)\n\n    def _initialize(self, agg_funs, val_var):\n        var_indep_funs, var_dep_funs = self.__group_aggregations(agg_funs)\n        self._create_group_tables(var_indep_funs, var_dep_funs)\n        self.__reference_aggregations(var_indep_funs, var_dep_funs)\n        self._create_pivot_tables(val_var)\n\n    def __group_aggregations(self, agg_funs):\n        auto_funcs = self.AutonomousFunctions\n        var_indep_funs = [fun for fun in agg_funs if fun in auto_funcs]\n        var_dep_funs = []\n        attrs = self._table.domain.variables + self._table.domain.metas\n        prod = product(filter_visible(attrs),\n                       [fun for fun in agg_funs if fun not in auto_funcs])\n        for var, fun in prod:\n            if self.__include_aggregation(fun, var):\n                var_dep_funs.append((var, fun))\n        return var_indep_funs, var_dep_funs\n\n    def __include_aggregation(self, fun, var):\n        return fun in self.ContVarFunctions and var.is_continuous or \\\n               fun in self.DiscVarFunctions and var.is_discrete or \\\n               fun in self.AnyVarFunctions\n\n    def __reference_aggregations(self, var_indep_funs, var_dep_funs):\n        self._indepen_agg_done = {}\n        self._depen_agg_done = defaultdict(dict)\n        i = 1 - int(bool(self.single_var_grouping))\n        for i, fun in enumerate(var_indep_funs, i + 1):\n            self._indepen_agg_done[fun] = i\n        for j, (var, fun) in enumerate(var_dep_funs, i + 1):\n            self._depen_agg_done[fun].update({var: j})\n\n    def _create_group_tables(self, var_indep_funs, var_dep_funs):\n        attrs = [ContinuousVariable(f\"({str(fun).lower()})\")\n                 for fun in var_indep_funs]\n        for var, fun in var_dep_funs:\n            name = f\"{var.name} ({str(fun).lower()})\"\n            if fun in self.DiscVarFunctions:\n                attrs.append(DiscreteVariable(name, var.values))\n            else:\n                if isinstance(var, TimeVariable) and \\\n                        fun in self.TimeVarFunctions:\n                    attrs.append(TimeVariable(name, have_date=var.have_date,\n                                              have_time=var.have_time))\n                else:\n                    attrs.append(ContinuousVariable(name))\n        args = (var_indep_funs, var_dep_funs, attrs)\n        for t, var in ((\"table\", None), (\"total_h\", self._col_var),\n                       (\"total_v\", self._row_var), (\"total\", self._total_var)):\n            setattr(self._group_tables, t, self.__get_group_table(var, *args))\n\n    def __get_group_table(self, var, var_indep_funs, var_dep_funs, attrs):\n        if var is self._total_var:\n            group_tab = self._group_tables.total\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._total_var]\n            combs = np.array([[0]])\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (~np.isnan(self._col_var_col)))[0]]\n        elif var is self._row_var or self.single_var_grouping:\n            group_tab = self._group_tables.total_v\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._row_var]\n            combs = self._row_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._col_var_col)) &\n                                     (self._row_var_col == x[0]))[0]]\n        elif var is self._col_var:\n            group_tab = self._group_tables.total_h\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._col_var]\n            combs = self._col_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (self._col_var_col == x[0]))[0]]\n        else:\n            group_tab = self._group_tables.table\n            offset = 0\n            leading_vars = [self._row_var, self._col_var]\n            combs = np.array(list(product(self._row_var_groups,\n                                          self._col_var_groups)))\n            sub_table_getter = lambda x: \\\n                self._table[np.where((self._row_var_col == x[0])\n                                     & (self._col_var_col == x[1]))[0]]\n\n        if not combs.shape[0]:\n            return None\n\n        n = len(var_indep_funs) + len(var_dep_funs)\n        X = np.zeros((len(combs), n), dtype=float)\n        for i, comb in enumerate(combs):\n            sub_table = sub_table_getter(comb)\n            j = -1\n            for j, fun in enumerate(var_indep_funs):\n                if fun in self._indepen_agg_done:\n                    # TODO - optimize - after this line is executed,\n                    # the whole column is already set\n                    X[:, j] = group_tab.X[:, self._indepen_agg_done[fun] - offset]\n                else:\n                    X[i, j] = fun(sub_table)\n            for k, (v, fun) in enumerate(var_dep_funs, j + 1):\n                if fun in self._depen_agg_done:\n                    X[:, k] = group_tab.X[:, self._depen_agg_done[fun][v] - offset]\n                else:\n                    X[i, k] = fun(sub_table.get_column_view(v)[0])\n\n        #rename leading vars (seems the easiest) if needed\n        current = [var.name for var in attrs]\n        uniq_leading_vars = []\n        for v in leading_vars:\n            uniq = get_unique_names(current, v.name)\n            if uniq != v.name:\n                self.renamed.append(v.name)\n                v = v.copy(name=uniq)\n            uniq_leading_vars.append(v)\n            current.append(uniq)\n\n        return Table(Domain(uniq_leading_vars + attrs), np.hstack((combs, X)))\n\n    def update_pivot_table(self, val_var: Variable):\n        self._create_pivot_tables(val_var)\n\n    def _create_pivot_tables(self, val_var):\n        if not self._group_tables.table:\n            self._pivot_tables = self.Tables()\n            return\n\n        agg_funs = [fun for fun in self._current_agg_functions\n                    if fun in self.AutonomousFunctions\n                    or val_var and self.__include_aggregation(fun, val_var)]\n        X, X_h, X_v, X_t = self.__get_pivot_tab_x(val_var, agg_funs)\n        dom, dom_h, dom_v, dom_t = self.__get_pivot_tab_domain(\n            val_var, X, X_h, X_v, X_t, agg_funs)\n        for t, d, x in ((\"table\", dom, X), (\"total_h\", dom_h, X_h),\n                        (\"total_v\", dom_v, X_v), (\"total\", dom_t, X_t)):\n            setattr(self._pivot_tables, t, Table(d, x))\n\n    # pylint: disable=invalid-name\n    def __get_pivot_tab_domain(self, val_var, X, X_h, X_v, X_t, agg_funs):\n        def map_values(index, _X):\n            values = np.unique(_X[:, index])\n            values = np.delete(values, np.where(values == \"nan\")[0])\n            for j, value in enumerate(values):\n                _X[:, index][_X[:, index] == value] = j\n            return values\n\n        create_time_var = \\\n            isinstance(val_var, TimeVariable) and \\\n            all(fun in self.TimeVarFunctions for fun in agg_funs)\n        create_cont_var = \\\n            not val_var or val_var.is_continuous and \\\n            (not isinstance(val_var, TimeVariable) or\n             all(fun in self.FloatFunctions for fun in agg_funs))\n\n        vals = np.array(self._col_var.values)[self._col_var_groups.astype(int)]\n        if create_time_var:\n            kwargs = {\"have_date\": val_var.have_date,\n                      \"have_time\": val_var.have_time}\n            attrs = [[TimeVariable(f\"{v}\", **kwargs) for v in vals]] * 2\n            attrs.extend([[TimeVariable(\"Total\", **kwargs)]] * 2)\n        elif create_cont_var:\n            attrs = [[ContinuousVariable(f\"{v}\", 1) for v in vals]] * 2\n            attrs.extend([[ContinuousVariable(\"Total\", 1)]] * 2)\n        else:\n            attrs = []\n            for x in (X, X_h):\n                attrs.append([DiscreteVariable(f\"{v}\", map_values(i, x))\n                              for i, v in enumerate(vals, 2)])\n            for x in (X_v, X_t):\n                attrs.append([DiscreteVariable(\"Total\", map_values(0, x))])\n        row_var_h = DiscreteVariable(self._row_var.name, values=[\"Total\"])\n        aggr_attr = DiscreteVariable('Aggregate', [str(f) for f in agg_funs])\n\n        same_row_col = self._col_var is self._row_var\n\n        extra_vars = [self._row_var, aggr_attr]\n        uniq_a = get_unique_names_duplicates([v.name for v in extra_vars]\n                                             + [atr.name for atr in attrs[0]])\n        for (idx, var), u in zip(enumerate(chain(extra_vars, attrs[0])), uniq_a):\n            if var.name == u:\n                continue\n            if idx == 0:\n                self.renamed.append(self._row_var.name)\n                self._row_var = self._row_var.copy(name=u)\n                if same_row_col:\n                    self._col_var = self._row_var\n                row_var_h = row_var_h.copy(name=u)\n            elif idx == 1:\n                self.renamed.append(aggr_attr.name)\n                aggr_attr = aggr_attr.copy(name=u)\n            else:\n                self.renamed.append(var.name)\n                attrs[0][idx-2] = var.copy(name=u)\n                attrs[1][idx-2] = var.copy(name=u)\n\n        if same_row_col:\n            vals = tuple(v.name for v in attrs[0])\n            self._row_var.make(self._row_var.name, values=vals)\n            vals = tuple(v.name for v in attrs[2])\n            row_var_h.make(row_var_h.name, vals)\n\n        return (Domain([self._row_var, aggr_attr] + attrs[0]),\n                Domain([row_var_h, aggr_attr] + attrs[1]),\n                Domain(attrs[2]), Domain(attrs[3]))\n\n    def __get_pivot_tab_x(self, val_var, agg_funs):\n        gt = self._group_tables\n        n_fun = len(agg_funs)\n        n_rows, n_cols = len(self._row_var_groups), len(self._col_var_groups)\n        is_float_type = not val_var or val_var.is_continuous\n        if isinstance(val_var, TimeVariable):\n            is_float_type = \\\n                all(fun in self.TimeVarFunctions for fun in agg_funs) or \\\n                all(fun in self.FloatFunctions for fun in agg_funs)\n        kwargs = {\"fill_value\": np.nan, \"dtype\": float} if is_float_type \\\n            else {\"fill_value\": \"\", \"dtype\": object}\n        X = np.full((n_rows * n_fun, 2 + n_cols), **kwargs)\n        X_h = np.full((n_fun, 2 + n_cols), **kwargs)\n        X_v = np.full((n_rows * n_fun, 1), **kwargs)\n        X_t = np.full((n_fun, 1), **kwargs)\n        for i, fun in enumerate(agg_funs):\n            args = (val_var, fun, is_float_type)\n            X[i::n_fun, 2:] = self.__rows_for_function(n_rows, n_cols, *args)\n            X[i::n_fun, :2] = np.array([[row_val, agg_funs.index(fun)] for\n                                        row_val in self._row_var_groups])\n            X_h[i, :2] = 0, agg_funs.index(fun)\n            X_h[i, 2:] = self.__total_for_function(gt.total_h, *args)\n            X_v[i::n_fun, 0] = self.__total_for_function(gt.total_v, *args)\n            X_t[i] = self.__total_for_function(gt.total, *args)\n        return X, X_h, X_v, X_t\n\n    def __total_for_function(self, group_tab, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        ref -= int(bool(not self.single_var_grouping))\n        return self.__check_continuous(val_var, group_tab.X[:, ref],\n                                       fun, is_float_type)\n\n    def __rows_for_function(self, n_rows, n_cols, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        column = self._group_tables.table.X[:, ref]\n        if self.single_var_grouping:\n            rows = np.full((n_rows, n_cols), fun(np.array([]), ), dtype=float)\n            rows[np.diag_indices_from(rows)] = column\n        else:\n            rows = column.reshape(n_rows, n_cols)\n        return self.__check_continuous(val_var, rows, fun, is_float_type)\n\n    def __check_continuous(self, val_var, column, fun, is_float_type):\n        if val_var and not val_var.is_continuous:\n            column = column.astype(str)\n            if fun in self.DiscVarFunctions:\n                for j, val in enumerate(val_var.values):\n                    column[column == str(float(j))] = val\n        elif isinstance(val_var, TimeVariable) and not is_float_type:\n            shape = column.shape\n            column = column.flatten()\n            column_ = column.astype(str)\n            if fun in self.TimeVarFunctions:\n                for i in range(column.shape[0]):\n                    if not np.isnan(column[i]):\n                        column_[i] = val_var.repr_val(column[i])\n            return column_.reshape(shape)\n        return column\n\n    @staticmethod\n    def count_defined(x):\n        if x.shape[0] == 0:\n            return 0\n        if x.size and np.issubdtype(x.dtype, np.number) and not sp.issparse(x):\n            nans = np.isnan(x).sum(axis=0)\n        elif sp.issparse(x) and x.size:\n            nans = np.bincount(x.nonzero()[1], minlength=x.shape[1])\n            x = x.tocsc()\n        else:\n            x_str = x.astype(str)\n            nans = ((x_str == \"nan\") | (x_str == \"\")).sum(axis=0) \\\n                if x.size else np.zeros(x.shape[1])\n        return x.shape[0] - nans\n\n    @staticmethod\n    def stat(x, f):\n        return f(x.astype(np.float), axis=0) if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def mode(x):\n        return Pivot.stat(x, nanmode).mode if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def majority(x):\n        if x.shape[0] == 0:\n            return np.nan\n        counts = bincount(x)[0]\n        return np.argmax(counts) if counts.shape[0] else np.nan\n\n    Count.func = lambda x: len(x[0])\n    Count_defined.func = lambda x: Pivot.count_defined(x[0])\n    Sum.func = lambda x: nansum(x[0], axis=0) if x[0].shape[0] > 0 else 0\n    Mean.func = lambda x: Pivot.stat(x[0], nanmean)\n    Min.func = lambda x: Pivot.stat(x[0], nanmin)\n    Max.func = lambda x: Pivot.stat(x[0], nanmax)\n    Median.func = lambda x: Pivot.stat(x[0], nanmedian)\n    Mode.func = lambda x: Pivot.mode(x[0])\n    Var.func = lambda x: Pivot.stat(x[0], nanvar)\n    Majority.func = lambda x: Pivot.majority(x[0])\n\n\nclass BorderedItemDelegate(QStyledItemDelegate):\n    def paint(self, painter, option, index):\n        \"\"\"Overloads `paint` to draw borders\"\"\"\n        QStyledItemDelegate.paint(self, painter, option, index)\n        if index.data(BorderRole):\n            painter.save()\n            painter.setPen(index.data(BorderColorRole))\n            rect = option.rect\n            painter.drawLine(rect.topLeft(), rect.topRight())\n            painter.restore()\n\n\nclass PivotTableView(QTableView):\n    selection_changed = pyqtSignal()\n\n    TOTAL_STRING = \"Total\"\n\n    def __init__(self):\n        super().__init__(editTriggers=QTableView.NoEditTriggers)\n        self._n_classesv = None  # number of row_feature values\n        self._n_classesh = None  # number of col_feature values\n        self._n_agg_func = None  # number of aggregation functions\n        self._n_leading_rows = None  # number of leading rows\n        self._n_leading_cols = None  # number of leading columns\n\n        self.table_model = QStandardItemModel(self)\n        self.setModel(self.table_model)\n        self.horizontalHeader().hide()\n        self.verticalHeader().hide()\n        self.horizontalHeader().setMinimumSectionSize(60)\n        self.setShowGrid(False)\n        self.setSizePolicy(QSizePolicy.MinimumExpanding,\n                           QSizePolicy.MinimumExpanding)\n        self.setItemDelegate(BorderedItemDelegate())\n        self.pressed.connect(self.__cell_clicked)\n        self.clicked.connect(self.__cell_clicked)\n        self.entered.connect(self.__cell_entered)\n        self.__clicked_cell = None\n\n    @property\n    def add_agg_column(self) -> bool:\n        return self._n_agg_func > 1\n\n    def __cell_entered(self, model_index):\n        if self.__clicked_cell is None:\n            return\n        index = self.table_model.index\n        selection = None\n        i_end, j_end = model_index.row(), model_index.column()\n        i_start, j_start = self.__clicked_cell\n        i_start, i_end = sorted([i_start, i_end])\n        j_start, j_end = sorted([j_start, j_end])\n        if i_start >= self._n_leading_rows and j_start >= self._n_leading_cols:\n            i_start = (i_start - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows\n            i_end = (i_end - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows + self._n_agg_func - 1\n            start, end = index(i_start, j_start), index(i_end, j_end)\n            selection = QItemSelection(start, end)\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n        self.selection_changed.emit()\n\n    def __cell_clicked(self, model_index):\n        i, j = model_index.row(), model_index.column()\n        self.__clicked_cell = (i, j)\n        m, n = self.table_model.rowCount(), self.table_model.columnCount()\n        index = self.table_model.index\n        selection = None\n        if i > m - self._n_agg_func - 1 and j == n - 1:\n            start_index = index(self._n_leading_rows, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(m - 1, n - 1))\n        elif i == self._n_leading_rows - 1 or i > m - self._n_agg_func - 1:\n            start_index = index(self._n_leading_rows, j)\n            selection = QItemSelection(start_index, index(m - 1, j))\n        elif j in (self._n_leading_cols - 1, n - 1, 1):\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            start_index = index(i_start, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(i_end, n - 1))\n        elif i >= self._n_leading_rows and j >= self._n_leading_cols:\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            selection = QItemSelection(index(i_start, j), index(i_end, j))\n\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n\n    def mouseReleaseEvent(self, e):\n        super().mouseReleaseEvent(e)\n        self.selection_changed.emit()\n\n    def update_table(self, titleh: str, titlev: str, table: Table,\n                     table_total_h: Table, table_total_v: Table,\n                     table_total: Table):\n        self.clear()\n        if not table:\n            return\n\n        self._initialize(table, table_total_h)\n        self._set_headers(titleh, titlev, table)\n        self._set_values(table[:, 2:])\n        self._set_totals(table_total_h[:, 2:], table_total_v, table_total)\n        self._draw_lines()\n        self._resize(table)\n\n    def _initialize(self, table, table_total_h):\n        self._n_classesv = int(len(table) / len(table_total_h))\n        self._n_classesh = table.X.shape[1] - 2\n        self._n_agg_func = len(table_total_h)\n        self._n_leading_rows = 2\n        self._n_leading_cols = 2 + int(len(table_total_h) > 1)\n\n    def _set_headers(self, titleh, titlev, table):\n        self.__set_horizontal_title(titleh)\n        self.__set_vertical_title(titlev)\n        self.__set_flags_title()\n        self.__set_horizontal_headers(table)\n        self.__set_vertical_headers(table)\n\n    def __set_horizontal_title(self, titleh):\n        item = QStandardItem()\n        item.setData(titleh, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignCenter)\n        self.table_model.setItem(0, self._n_leading_cols, item)\n        self.setSpan(0, self._n_leading_cols, 1, self._n_classesh + 3)\n\n    def __set_vertical_title(self, titlev):\n        item = QStandardItem()\n        item.setData(titlev, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)\n        self.setItemDelegateForColumn(0, gui.VerticalItemDelegate(extend=True))\n        self.table_model.setItem(self._n_leading_rows, 0, item)\n        row_span = self._n_classesv * self._n_agg_func + 1\n        self.setSpan(self._n_leading_rows, 0, row_span, 1)\n\n    def __set_flags_title(self):\n        item = self.table_model.item(0, self._n_leading_cols)\n        item.setFlags(Qt.NoItemFlags)\n        item = self.table_model.item(self._n_leading_rows, 0)\n        item.setFlags(Qt.NoItemFlags)\n        for i, j in product(range(self._n_leading_rows),\n                            range(self._n_leading_cols)):\n            item = QStandardItem()\n            item.setFlags(Qt.NoItemFlags)\n            self.table_model.setItem(i, j, item)\n\n    def __set_horizontal_headers(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if not self.add_agg_column:\n            labels[0] = str(table[0, 1])\n        for i, label in enumerate(labels, self._n_leading_cols - 1):\n            self.table_model.setItem(1, i, self._create_header_item(label))\n\n    def __set_vertical_headers(self, table):\n        labels = [(str(row[0]), str(row[1])) for row in table]\n        i = self._n_leading_rows - 1\n        for i, (l1, l2) in enumerate(labels, self._n_leading_rows):\n            l1 = \"\" if (i - self._n_leading_rows) % self._n_agg_func else l1\n            self.table_model.setItem(i, 1, self._create_header_item(l1))\n            if self.add_agg_column:\n                self.table_model.setItem(i, 2, self._create_header_item(l2))\n\n        if self.add_agg_column:\n            labels = [str(row[1]) for row in table[:self._n_agg_func]]\n            start = self._n_leading_rows + self._n_agg_func * self._n_classesv\n            for j, l2 in enumerate(labels, i + 1):\n                l1 = self.TOTAL_STRING if j == start else \"\"\n                self.table_model.setItem(j, 1, self._create_header_item(l1))\n                self.table_model.setItem(j, 2, self._create_header_item(l2))\n        else:\n            item = self._create_header_item(self.TOTAL_STRING)\n            self.table_model.setItem(i + 1, 1, item)\n\n    def _set_values(self, table):\n        for i, j in product(range(len(table)), range(len(table[0]))):\n            value = table[i, j]\n            item = self._create_value_item(str(value))\n            self.table_model.setItem(i + self._n_leading_rows,\n                                     j + self._n_leading_cols, item)\n\n    def _set_totals(self, table_total_h, table_total_v, table_total):\n        def set_total_item(table, get_row, get_col):\n            for i, j in product(range(len(table)), range(len(table[0]))):\n                item = self._create_header_item(str(table[i, j]))\n                self.table_model.setItem(get_row(i), get_col(j), item)\n\n        last_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        last_col = self._n_leading_cols + self._n_classesh\n        set_total_item(table_total_v, lambda x: x + self._n_leading_rows,\n                       lambda x: last_col)\n        set_total_item(table_total_h, lambda x: x + last_row,\n                       lambda x: x + self._n_leading_cols)\n        set_total_item(table_total, lambda x: x + last_row, lambda x: last_col)\n\n    def _create_header_item(self, text):\n        bold_font = self.table_model.invisibleRootItem().font()\n        bold_font.setBold(True)\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setFont(bold_font)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled)\n        return item\n\n    @staticmethod\n    def _create_value_item(text):\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)\n        return item\n\n    def _draw_lines(self):\n        end_col = self._n_leading_cols + self._n_classesh + 1\n        total_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        indices = [(total_row, j) for j in range(1, end_col)]\n        for i in range(self._n_classesv):\n            inner_row = self._n_agg_func * i + self._n_leading_rows\n            inner_indices = [(inner_row, j) for j in range(1, end_col)]\n            indices = indices + inner_indices\n            if not self.add_agg_column:\n                break\n        for i, j in indices:\n            item = self.table_model.item(i, j)\n            item.setData(\"t\", BorderRole)\n            item.setData(QColor(160, 160, 160), BorderColorRole)\n\n    def _resize(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if len(' '.join(labels)) < 120:\n            self.horizontalHeader().setSectionResizeMode(\n                QHeaderView.ResizeToContents)\n        else:\n            self.horizontalHeader().setDefaultSectionSize(60)\n\n    def get_selection(self) -> Set:\n        m, n = self._n_leading_rows, self._n_leading_cols\n        return {(ind.row() - m, ind.column() - n)\n                for ind in self.selectedIndexes()}\n\n    def set_selection(self, indexes: Set):\n        selection = QItemSelection()\n        index = self.model().index\n        for row, col in indexes:\n            sel = index(row + self._n_leading_rows, col + self._n_leading_cols)\n            selection.select(sel, sel)\n        self.selectionModel().select(\n            selection, QItemSelectionModel.ClearAndSelect)\n\n    def clear(self):\n        self.table_model.clear()\n\n\nclass OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            allvars = domain.variables + domain.metas\n            self.val_feature = allvars[0] if allvars[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)\n\n\nif __name__ == \"__main__\":\n    WidgetPreview(OWPivot).run(set_data=Table(\"heart_disease\"))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/data/owpivot.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "6f3074a77",
                "new_commit_hash": "8906c252f",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/data/owpivot.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/data/owpivot.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 921,
                            "length": 8
                        },
                        "new_range": {
                            "start": 921,
                            "length": 8
                        },
                        "section": "class OWPivot(OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            self.row_feature = model[0]",
                                "type": "context"
                            },
                            {
                                "content": "        model = self.controls.val_feature.model()",
                                "type": "context"
                            },
                            {
                                "content": "        if model and len(model) > 2:",
                                "type": "context"
                            },
                            {
                                "content": "            self.val_feature = domain.variables[0] \\",
                                "type": "deleted"
                            },
                            {
                                "content": "                if domain.variables[0] in model else model[2]",
                                "type": "deleted"
                            },
                            {
                                "content": "            allvars = domain.variables + domain.metas",
                                "type": "added"
                            },
                            {
                                "content": "            self.val_feature = allvars[0] if allvars[0] in model else model[2]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def commit(self):",
                                "type": "context"
                            },
                            {
                                "content": "        def send_outputs(pivot_table, filtered_data, grouped_data):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/owpivot.py",
                            "type": "class",
                            "name": "OWPivot",
                            "content": "class OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n                if domain.variables[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 725,
                            "end_lineno": 1023,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/data/owpivot.py",
                            "type": "method",
                            "name": "OWPivot.init_attr_values",
                            "content": "class OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n                if domain.variables[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 914,
                            "end_lineno": 925,
                            "parent": {
                                "file_name": "Orange/widgets/data/owpivot.py",
                                "type": "class",
                                "name": "OWPivot",
                                "content": "class OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n                if domain.variables[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 725,
                                "end_lineno": 1023,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring,unsubscriptable-object\nimport unittest\nfrom unittest.mock import patch\nfrom pickle import loads, dumps\n\nimport numpy as np\n\nfrom AnyQt.QtCore import Qt, QPoint\nfrom AnyQt.QtTest import QTest\n\nfrom Orange.data import (Table, Domain, ContinuousVariable as Cv,\n                         StringVariable as sv, DiscreteVariable as Dv,\n                         TimeVariable as Tv)\nfrom Orange.widgets.data.owpivot import (OWPivot, Pivot,\n                                         AggregationFunctionsEnum)\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import simulate\n\n\nclass TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)\n\n\nclass TestAggregationFunctionsEnum(unittest.TestCase):\n    def test_pickle(self):\n        self.assertIs(AggregationFunctionsEnum.Sum,\n                      loads(dumps(AggregationFunctionsEnum.Sum)))\n\n    def test_sort(self):\n        af = AggregationFunctionsEnum\n        self.assertEqual(sorted([af.Sum, af.Min]), sorted([af.Min, af.Sum]))\n\n\nclass TestPivot(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")),\n                         Dv(\"d2\", (\"c\", \"d\", \"e\")), Cv(\"c1\")])\n        X = np.array([[0, 0, 1], [0, 1, 2], [0, np.nan, 3], [0, 0, 4],\n                      [1, 0, 5], [1, 0, 6], [1, 1, np.nan], [1, 2, 7],\n                      [np.nan, 0, 8]])\n        cls.table = Table(domain, X)\n\n        domain = Domain([Cv(\"c0\"), Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")],\n                        Dv(\"cls\", (\"a\", \"b\")), [sv(\"m1\"), sv(\"m2\")])\n        X = np.array([[np.nan, 0, 1, 0, 2], [np.nan, 1, 2, np.nan, 3],\n                      [np.nan, 0, 3, 1, np.nan]])\n        M = np.array([[\"aa\", \"dd\"], [\"bb\", \"ee\"], [\"cc\", \"\"]], dtype=object)\n        cls.table1 = Table(domain, X, np.array([0, 0, 1]), M)\n\n    def test_group_table(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(group_tab, Table(Domain(domain[:2] + atts), X))\n\n    def test_group_table_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        table = Table(domain, X)\n        pivot = Pivot(table, Pivot.Functions, domain[0], val_var=domain[1])\n        str_grouped = \\\n            \"[[a, 2, 2, a, 2, 1.1e+09, 1987-06-06, 1973-03-03, \" \\\n            \"2001-09-09, 1973-03-03, 1987-06-06, 2.025e+17],\\n \" \\\n            \"[b, 2, 2, b, 1, 2e+08, 1976-05-03, 1976-05-03, \" \\\n            \"1976-05-03, 1976-05-03, 1976-05-03, 0]]\"\n        self.assertEqual(str(pivot.group_table), str_grouped)\n\n    def test_group_table_metas(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n        table.metas = table.metas.astype(object)\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Count.func\",\n           side_effect=Pivot.Count.func)\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Sum.func\",\n           side_effect=Pivot.Sum.func)\n    def test_group_table_use_cached(self, count_func, sum_func):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count, Pivot.Sum], domain[0], domain[1])\n        group_tab = pivot.group_table\n        count_func.reset_mock()\n        sum_func.reset_mock()\n\n        pivot.update_group_table(Pivot.Functions)\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(pivot.group_table,\n                                Table(Domain(domain[:2] + atts), X))\n\n        pivot.update_group_table([Pivot.Count, Pivot.Sum])\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        self.assert_table_equal(pivot.group_table, group_tab)\n\n    def test_group_table_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        domain = Domain(domain[:1] + atts)\n        X = np.array([[0, 4, 4, 0, 3, 0, 4, 10, 2.5, 1, 4, 1, 2.5, 1.25],\n                      [1, 4, 4, 1, 4, 0, 3, 18, 6, 5, 7, 5, 6, 2 / 3]],\n                     dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(domain[:1] + atts), X))\n\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[0])\n        group_tab_same_vars = pivot.group_table\n        self.assert_table_equal(group_tab, group_tab_same_vars)\n\n    def test_group_table_no_col_var_metas(self):\n        for var in self.table1.domain.metas:\n            self.assertRaises(TypeError, Pivot, self.table1, var)\n\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    def test_group_table_update(self):\n        domain = self.table.domain\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        table = Table(Domain(domain[:2] + atts), X)\n\n        agg = [Pivot.Functions.Count, Pivot.Functions.Sum]\n        pivot = Pivot(self.table, agg, domain[0], domain[1])\n        group_tab = pivot.group_table\n        pivot.update_group_table(Pivot.Functions)\n        self.assert_table_equal(pivot.group_table, table)\n        pivot.update_group_table(agg)\n        self.assert_table_equal(group_tab, pivot.group_table)\n\n    def test_group_table_1(self):\n        var = self.table1.domain.variables[1]\n        domain = Domain(\n            [var, Cv(\"(count)\"), Cv(\"c0 (count defined)\"), Cv(\"c0 (sum)\"),\n             Cv(\"c0 (mean)\"), Cv(\"c0 (min)\"), Cv(\"c0 (max)\"), Cv(\"c0 (mode)\"),\n             Cv(\"c0 (median)\"), Cv(\"c0 (var)\"), Cv(\"d1 (count defined)\"),\n             Dv(\"d1 (majority)\", [\"a\", \"b\"]), Cv(\"c1 (count defined)\"),\n             Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n             Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"),\n             Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"a\", \"b\"]),\n             Cv(\"c2 (count defined)\"), Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"),\n             Cv(\"c2 (min)\"), Cv(\"c2 (max)\"), Cv(\"c2 (mode)\"),\n             Cv(\"c2 (median)\"), Cv(\"c2 (var)\"), Cv(\"cls (count defined)\"),\n             Dv(\"cls (majority)\", [\"a\", \"b\"]), Cv(\"m1 (count defined)\"),\n             Cv(\"m2 (count defined)\")])\n        X = np.array([[0, 2, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 2, 0, 2, 4, 2, 1, 3, 1, 2, 1,\n                       2, 0, 1, 2, 2, 2, 2, 2, 2, 0, 2, 0, 2, 1],\n                      [1, 1, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0,\n                       np.nan, 1, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1, 1]])\n        pivot = Pivot(self.table1, Pivot.Functions, var)\n        group_tab = pivot.group_table\n        self.assert_table_equal(group_tab, Table(domain, X))\n\n    def test_group_sparse_data(self):\n        var = self.table1.domain.variables[1]\n        dense = Pivot(self.table1, Pivot.Functions, var)\n        sparse_data = self.table1.to_sparse()\n        var = sparse_data.domain.variables[1]\n        sparse = Pivot(sparse_data, Pivot.Functions, var)\n        self.assert_table_equal(dense.group_table, sparse.group_table)\n\n    def test_pivot(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1], domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                                 \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0],\n                      [0, 1, 2, 1, 0],\n                      [0, 2, 5, 2, 0],\n                      [0, 3, 2.5, 2, np.nan],\n                      [0, 4, 1, 2, np.nan],\n                      [0, 5, 4, 2, np.nan],\n                      [0, 6, 1, 2, np.nan],\n                      [0, 7, 2.5, 2, np.nan],\n                      [0, 8, 2.25, 0, np.nan],\n                      [1, 0, 2, 1, 1],\n                      [1, 1, 2, 0, 1],\n                      [1, 2, 11, 0, 7],\n                      [1, 3, 5.5, np.nan, 7],\n                      [1, 4, 5, np.nan, 7],\n                      [1, 5, 6, np.nan, 7],\n                      [1, 6, 5, np.nan, 7],\n                      [1, 7, 5.5, np.nan, 7],\n                      [1, 8, 0.25, np.nan, 0]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_total(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count, Pivot.Functions.Sum],\n                      domain[0], domain[1], domain[2])\n\n        atts = (Dv(domain[0].name, [\"Total\"]),\n                Dv(\"Aggregate\", [\"Count\", \"Sum\"]), Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 4, 2, 1], [0, 1, 16, 2, 7]])\n        table = Table(Domain(atts), X)\n\n        self.assert_table_equal(pivot.pivot_total_h, table)\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[3], [7], [4], [18]]))\n        self.assert_table_equal(pivot.pivot_total_v, table)\n\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[7], [25]]))\n        self.assert_table_equal(pivot.pivot_total, table)\n\n    def test_pivot_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], None, domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\",\n                   [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                    \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"a\"), Cv(\"b\"))\n        X = np.array([[0, 0, 4, 0],\n                      [0, 1, 4, 0],\n                      [0, 2, 10, 0],\n                      [0, 3, 2.5, np.nan],\n                      [0, 4, 1, np.nan],\n                      [0, 5, 4, np.nan],\n                      [0, 6, 1, np.nan],\n                      [0, 7, 2.5, np.nan],\n                      [0, 8, 1.25, np.nan],\n                      [1, 0, 0, 4],\n                      [1, 1, 0, 3],\n                      [1, 2, 0, 18],\n                      [1, 3, np.nan, 6],\n                      [1, 4, np.nan, 5],\n                      [1, 5, np.nan, 7],\n                      [1, 6, np.nan, 5],\n                      [1, 7, np.nan, 6],\n                      [1, 8, np.nan, 2 / 3]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_no_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0], [1, 0, 2, 1, 1]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_disc_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count_defined, Pivot.Majority],\n                      domain[2], domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (domain[2], Dv(\"Aggregate\", [\"Count defined\", \"Majority\"]),\n                Dv(\"a\", [\"0.0\", \"1.0\", \"c\", \"d\"]),\n                Dv(\"b\", [\"0.0\", \"1.0\", \"c\", \"e\"]))\n        X = np.array([[1, 0, 1, 0],\n                      [1, 1, 2, np.nan],\n                      [2, 0, 1, 0],\n                      [2, 1, 3, np.nan],\n                      [3, 0, 0, 0],\n                      [3, 1, np.nan, np.nan],\n                      [4, 0, 1, 0],\n                      [4, 1, 2, np.nan],\n                      [5, 0, 0, 1],\n                      [5, 1, np.nan, 2],\n                      [6, 0, 0, 1],\n                      [6, 1, np.nan, 2],\n                      [7, 0, 0, 1],\n                      [7, 1, np.nan, 3],\n                      [8, 0, 0, 0],\n                      [8, 1, np.nan, np.nan]])\n        self.assert_table_equal(pivot_tab, Table(Domain(atts), X))\n\n    def test_pivot_time_val_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Dv(\"d2\", (\"c\", \"d\")),\n                         Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1, 1e9], [0, 0, 1e8], [1, 0, 2e8], [1, 1, np.nan]])\n        table = Table(domain, X)\n\n        # Min\n        pivot = Pivot(table, [Pivot.Min],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Min, Max\n        pivot = Pivot(table, [Pivot.Min, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\", \"Max\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan],\n                      [1, 1, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Sum\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Sum],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Sum\"]),\n                Cv(\"c\"), Cv(\"d\"))\n        X = np.array([[0, 0, 1, 1],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 1, 0],\n                      [1, 1, 2e8, 0]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Max\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Max\"]),\n                Dv(\"c\", [\"1.0\", \"1973-03-03\", \"1976-05-03\"]),\n                Dv(\"d\", [\"0.0\", \"1.0\", \"2001-09-09\"]))\n        X = np.array([[0, 0, 0, 1],\n                      [0, 1, 1, 2],\n                      [1, 0, 0, 0],\n                      [1, 1, 2, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n    def test_pivot_attr_combinations(self):\n        domain = self.table1.domain\n        for var1, var2, var3 in ((domain[1], domain[3], domain[5]),  # d d d\n                                 (domain[1], domain[3], domain[4]),  # d d c\n                                 (domain[1], domain[3], domain[-1]),  # d d s\n                                 (domain[2], domain[3], domain[5]),  # c d d\n                                 (domain[2], domain[3], domain[4]),  # c d c\n                                 (domain[2], domain[3], domain[-1])):  # c d s\n            pivot = Pivot(self.table1, Pivot.Functions, var1, var2, var3)\n            pivot_tab = pivot.pivot_table\n            self.assertGreaterEqual(pivot_tab.X.shape[0], 4)\n            self.assertGreaterEqual(pivot_tab.X.shape[1], 4)\n        for var1, var2 in ((domain[1], domain[2]),  # d c\n                           (domain[1], domain[-2]),  # d s\n                           (domain[2], domain[4]),  # c\n                           (domain[-1], domain[1])):  # s\n            self.assertRaises(TypeError, Pivot, self.table1, var1, var2)\n\n    def test_pivot_update(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count], domain[0],\n                      domain[1], domain[2])\n        pivot_tab1 = pivot.pivot_table\n        pivot.update_pivot_table(domain[1])\n        pivot.update_pivot_table(domain[2])\n        self.assert_table_equal(pivot_tab1, pivot.pivot_table)\n\n    def test_pivot_data_subset(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var\n        pivot = Pivot(data[:100], Pivot.Functions, cls_var, None, cls_var)\n        atts = (cls_var, Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Majority\"]),\n                Dv(\"Iris-setosa\", [\"0.0\", \"50.0\", \"Iris-setosa\"]),\n                Dv(\"Iris-versicolor\", [\"0.0\", \"50.0\", \"Iris-versicolor\"]))\n        domain = Domain(atts)\n        self.assert_domain_equal(domain, pivot.pivot_table.domain)\n\n    def test_pivot_renaming_domain(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        pivot = Pivot(data, [Pivot.Functions.Sum], cls_var, None, None)\n\n        renamed_var = data.domain.class_var.copy(name='Aggregate (1)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n        renamed_var = data.domain.class_var.copy(name='Aggregate (2)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n\n    def assert_table_equal(self, table1, table2):\n        self.assert_domain_equal(table1.domain, table2.domain)\n        np.testing.assert_array_equal(table1.X, table2.X)\n        np.testing.assert_array_equal(table1.Y, table2.Y)\n        np.testing.assert_array_equal(table1.metas, table2.metas)\n\n    def assert_domain_equal(self, domain1, domain2):\n        for var1, var2 in zip(domain1.variables + domain1.metas,\n                              domain2.variables + domain2.metas):\n            self.assertEqual(type(var1), type(var2))\n            self.assertEqual(var1.name, var2.name)\n            if var1.is_discrete:\n                self.assertEqual(var1.values, var2.values)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "new_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring,unsubscriptable-object\nimport unittest\nfrom unittest.mock import patch\nfrom pickle import loads, dumps\n\nimport numpy as np\n\nfrom AnyQt.QtCore import Qt, QPoint\nfrom AnyQt.QtTest import QTest\n\nfrom Orange.data import (Table, Domain, ContinuousVariable as Cv,\n                         StringVariable as sv, DiscreteVariable as Dv,\n                         TimeVariable as Tv)\nfrom Orange.widgets.data.owpivot import (OWPivot, Pivot,\n                                         AggregationFunctionsEnum)\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import simulate\n\n\nclass TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n\n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)\n\n\nclass TestAggregationFunctionsEnum(unittest.TestCase):\n    def test_pickle(self):\n        self.assertIs(AggregationFunctionsEnum.Sum,\n                      loads(dumps(AggregationFunctionsEnum.Sum)))\n\n    def test_sort(self):\n        af = AggregationFunctionsEnum\n        self.assertEqual(sorted([af.Sum, af.Min]), sorted([af.Min, af.Sum]))\n\n\nclass TestPivot(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")),\n                         Dv(\"d2\", (\"c\", \"d\", \"e\")), Cv(\"c1\")])\n        X = np.array([[0, 0, 1], [0, 1, 2], [0, np.nan, 3], [0, 0, 4],\n                      [1, 0, 5], [1, 0, 6], [1, 1, np.nan], [1, 2, 7],\n                      [np.nan, 0, 8]])\n        cls.table = Table(domain, X)\n\n        domain = Domain([Cv(\"c0\"), Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")],\n                        Dv(\"cls\", (\"a\", \"b\")), [sv(\"m1\"), sv(\"m2\")])\n        X = np.array([[np.nan, 0, 1, 0, 2], [np.nan, 1, 2, np.nan, 3],\n                      [np.nan, 0, 3, 1, np.nan]])\n        M = np.array([[\"aa\", \"dd\"], [\"bb\", \"ee\"], [\"cc\", \"\"]], dtype=object)\n        cls.table1 = Table(domain, X, np.array([0, 0, 1]), M)\n\n    def test_group_table(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(group_tab, Table(Domain(domain[:2] + atts), X))\n\n    def test_group_table_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        table = Table(domain, X)\n        pivot = Pivot(table, Pivot.Functions, domain[0], val_var=domain[1])\n        str_grouped = \\\n            \"[[a, 2, 2, a, 2, 1.1e+09, 1987-06-06, 1973-03-03, \" \\\n            \"2001-09-09, 1973-03-03, 1987-06-06, 2.025e+17],\\n \" \\\n            \"[b, 2, 2, b, 1, 2e+08, 1976-05-03, 1976-05-03, \" \\\n            \"1976-05-03, 1976-05-03, 1976-05-03, 0]]\"\n        self.assertEqual(str(pivot.group_table), str_grouped)\n\n    def test_group_table_metas(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n        table.metas = table.metas.astype(object)\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Count.func\",\n           side_effect=Pivot.Count.func)\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Sum.func\",\n           side_effect=Pivot.Sum.func)\n    def test_group_table_use_cached(self, count_func, sum_func):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count, Pivot.Sum], domain[0], domain[1])\n        group_tab = pivot.group_table\n        count_func.reset_mock()\n        sum_func.reset_mock()\n\n        pivot.update_group_table(Pivot.Functions)\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(pivot.group_table,\n                                Table(Domain(domain[:2] + atts), X))\n\n        pivot.update_group_table([Pivot.Count, Pivot.Sum])\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        self.assert_table_equal(pivot.group_table, group_tab)\n\n    def test_group_table_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        domain = Domain(domain[:1] + atts)\n        X = np.array([[0, 4, 4, 0, 3, 0, 4, 10, 2.5, 1, 4, 1, 2.5, 1.25],\n                      [1, 4, 4, 1, 4, 0, 3, 18, 6, 5, 7, 5, 6, 2 / 3]],\n                     dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(domain[:1] + atts), X))\n\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[0])\n        group_tab_same_vars = pivot.group_table\n        self.assert_table_equal(group_tab, group_tab_same_vars)\n\n    def test_group_table_no_col_var_metas(self):\n        for var in self.table1.domain.metas:\n            self.assertRaises(TypeError, Pivot, self.table1, var)\n\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    def test_group_table_update(self):\n        domain = self.table.domain\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        table = Table(Domain(domain[:2] + atts), X)\n\n        agg = [Pivot.Functions.Count, Pivot.Functions.Sum]\n        pivot = Pivot(self.table, agg, domain[0], domain[1])\n        group_tab = pivot.group_table\n        pivot.update_group_table(Pivot.Functions)\n        self.assert_table_equal(pivot.group_table, table)\n        pivot.update_group_table(agg)\n        self.assert_table_equal(group_tab, pivot.group_table)\n\n    def test_group_table_1(self):\n        var = self.table1.domain.variables[1]\n        domain = Domain(\n            [var, Cv(\"(count)\"), Cv(\"c0 (count defined)\"), Cv(\"c0 (sum)\"),\n             Cv(\"c0 (mean)\"), Cv(\"c0 (min)\"), Cv(\"c0 (max)\"), Cv(\"c0 (mode)\"),\n             Cv(\"c0 (median)\"), Cv(\"c0 (var)\"), Cv(\"d1 (count defined)\"),\n             Dv(\"d1 (majority)\", [\"a\", \"b\"]), Cv(\"c1 (count defined)\"),\n             Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n             Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"),\n             Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"a\", \"b\"]),\n             Cv(\"c2 (count defined)\"), Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"),\n             Cv(\"c2 (min)\"), Cv(\"c2 (max)\"), Cv(\"c2 (mode)\"),\n             Cv(\"c2 (median)\"), Cv(\"c2 (var)\"), Cv(\"cls (count defined)\"),\n             Dv(\"cls (majority)\", [\"a\", \"b\"]), Cv(\"m1 (count defined)\"),\n             Cv(\"m2 (count defined)\")])\n        X = np.array([[0, 2, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 2, 0, 2, 4, 2, 1, 3, 1, 2, 1,\n                       2, 0, 1, 2, 2, 2, 2, 2, 2, 0, 2, 0, 2, 1],\n                      [1, 1, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0,\n                       np.nan, 1, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1, 1]])\n        pivot = Pivot(self.table1, Pivot.Functions, var)\n        group_tab = pivot.group_table\n        self.assert_table_equal(group_tab, Table(domain, X))\n\n    def test_group_sparse_data(self):\n        var = self.table1.domain.variables[1]\n        dense = Pivot(self.table1, Pivot.Functions, var)\n        sparse_data = self.table1.to_sparse()\n        var = sparse_data.domain.variables[1]\n        sparse = Pivot(sparse_data, Pivot.Functions, var)\n        self.assert_table_equal(dense.group_table, sparse.group_table)\n\n    def test_pivot(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1], domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                                 \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0],\n                      [0, 1, 2, 1, 0],\n                      [0, 2, 5, 2, 0],\n                      [0, 3, 2.5, 2, np.nan],\n                      [0, 4, 1, 2, np.nan],\n                      [0, 5, 4, 2, np.nan],\n                      [0, 6, 1, 2, np.nan],\n                      [0, 7, 2.5, 2, np.nan],\n                      [0, 8, 2.25, 0, np.nan],\n                      [1, 0, 2, 1, 1],\n                      [1, 1, 2, 0, 1],\n                      [1, 2, 11, 0, 7],\n                      [1, 3, 5.5, np.nan, 7],\n                      [1, 4, 5, np.nan, 7],\n                      [1, 5, 6, np.nan, 7],\n                      [1, 6, 5, np.nan, 7],\n                      [1, 7, 5.5, np.nan, 7],\n                      [1, 8, 0.25, np.nan, 0]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_total(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count, Pivot.Functions.Sum],\n                      domain[0], domain[1], domain[2])\n\n        atts = (Dv(domain[0].name, [\"Total\"]),\n                Dv(\"Aggregate\", [\"Count\", \"Sum\"]), Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 4, 2, 1], [0, 1, 16, 2, 7]])\n        table = Table(Domain(atts), X)\n\n        self.assert_table_equal(pivot.pivot_total_h, table)\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[3], [7], [4], [18]]))\n        self.assert_table_equal(pivot.pivot_total_v, table)\n\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[7], [25]]))\n        self.assert_table_equal(pivot.pivot_total, table)\n\n    def test_pivot_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], None, domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\",\n                   [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                    \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"a\"), Cv(\"b\"))\n        X = np.array([[0, 0, 4, 0],\n                      [0, 1, 4, 0],\n                      [0, 2, 10, 0],\n                      [0, 3, 2.5, np.nan],\n                      [0, 4, 1, np.nan],\n                      [0, 5, 4, np.nan],\n                      [0, 6, 1, np.nan],\n                      [0, 7, 2.5, np.nan],\n                      [0, 8, 1.25, np.nan],\n                      [1, 0, 0, 4],\n                      [1, 1, 0, 3],\n                      [1, 2, 0, 18],\n                      [1, 3, np.nan, 6],\n                      [1, 4, np.nan, 5],\n                      [1, 5, np.nan, 7],\n                      [1, 6, np.nan, 5],\n                      [1, 7, np.nan, 6],\n                      [1, 8, np.nan, 2 / 3]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_no_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0], [1, 0, 2, 1, 1]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_disc_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count_defined, Pivot.Majority],\n                      domain[2], domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (domain[2], Dv(\"Aggregate\", [\"Count defined\", \"Majority\"]),\n                Dv(\"a\", [\"0.0\", \"1.0\", \"c\", \"d\"]),\n                Dv(\"b\", [\"0.0\", \"1.0\", \"c\", \"e\"]))\n        X = np.array([[1, 0, 1, 0],\n                      [1, 1, 2, np.nan],\n                      [2, 0, 1, 0],\n                      [2, 1, 3, np.nan],\n                      [3, 0, 0, 0],\n                      [3, 1, np.nan, np.nan],\n                      [4, 0, 1, 0],\n                      [4, 1, 2, np.nan],\n                      [5, 0, 0, 1],\n                      [5, 1, np.nan, 2],\n                      [6, 0, 0, 1],\n                      [6, 1, np.nan, 2],\n                      [7, 0, 0, 1],\n                      [7, 1, np.nan, 3],\n                      [8, 0, 0, 0],\n                      [8, 1, np.nan, np.nan]])\n        self.assert_table_equal(pivot_tab, Table(Domain(atts), X))\n\n    def test_pivot_time_val_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Dv(\"d2\", (\"c\", \"d\")),\n                         Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1, 1e9], [0, 0, 1e8], [1, 0, 2e8], [1, 1, np.nan]])\n        table = Table(domain, X)\n\n        # Min\n        pivot = Pivot(table, [Pivot.Min],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Min, Max\n        pivot = Pivot(table, [Pivot.Min, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\", \"Max\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan],\n                      [1, 1, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Sum\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Sum],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Sum\"]),\n                Cv(\"c\"), Cv(\"d\"))\n        X = np.array([[0, 0, 1, 1],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 1, 0],\n                      [1, 1, 2e8, 0]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Max\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Max\"]),\n                Dv(\"c\", [\"1.0\", \"1973-03-03\", \"1976-05-03\"]),\n                Dv(\"d\", [\"0.0\", \"1.0\", \"2001-09-09\"]))\n        X = np.array([[0, 0, 0, 1],\n                      [0, 1, 1, 2],\n                      [1, 0, 0, 0],\n                      [1, 1, 2, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n    def test_pivot_attr_combinations(self):\n        domain = self.table1.domain\n        for var1, var2, var3 in ((domain[1], domain[3], domain[5]),  # d d d\n                                 (domain[1], domain[3], domain[4]),  # d d c\n                                 (domain[1], domain[3], domain[-1]),  # d d s\n                                 (domain[2], domain[3], domain[5]),  # c d d\n                                 (domain[2], domain[3], domain[4]),  # c d c\n                                 (domain[2], domain[3], domain[-1])):  # c d s\n            pivot = Pivot(self.table1, Pivot.Functions, var1, var2, var3)\n            pivot_tab = pivot.pivot_table\n            self.assertGreaterEqual(pivot_tab.X.shape[0], 4)\n            self.assertGreaterEqual(pivot_tab.X.shape[1], 4)\n        for var1, var2 in ((domain[1], domain[2]),  # d c\n                           (domain[1], domain[-2]),  # d s\n                           (domain[2], domain[4]),  # c\n                           (domain[-1], domain[1])):  # s\n            self.assertRaises(TypeError, Pivot, self.table1, var1, var2)\n\n    def test_pivot_update(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count], domain[0],\n                      domain[1], domain[2])\n        pivot_tab1 = pivot.pivot_table\n        pivot.update_pivot_table(domain[1])\n        pivot.update_pivot_table(domain[2])\n        self.assert_table_equal(pivot_tab1, pivot.pivot_table)\n\n    def test_pivot_data_subset(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var\n        pivot = Pivot(data[:100], Pivot.Functions, cls_var, None, cls_var)\n        atts = (cls_var, Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Majority\"]),\n                Dv(\"Iris-setosa\", [\"0.0\", \"50.0\", \"Iris-setosa\"]),\n                Dv(\"Iris-versicolor\", [\"0.0\", \"50.0\", \"Iris-versicolor\"]))\n        domain = Domain(atts)\n        self.assert_domain_equal(domain, pivot.pivot_table.domain)\n\n    def test_pivot_renaming_domain(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        pivot = Pivot(data, [Pivot.Functions.Sum], cls_var, None, None)\n\n        renamed_var = data.domain.class_var.copy(name='Aggregate (1)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n        renamed_var = data.domain.class_var.copy(name='Aggregate (2)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n\n    def assert_table_equal(self, table1, table2):\n        self.assert_domain_equal(table1.domain, table2.domain)\n        np.testing.assert_array_equal(table1.X, table2.X)\n        np.testing.assert_array_equal(table1.Y, table2.Y)\n        np.testing.assert_array_equal(table1.metas, table2.metas)\n\n    def assert_domain_equal(self, domain1, domain2):\n        for var1, var2 in zip(domain1.variables + domain1.metas,\n                              domain2.variables + domain2.metas):\n            self.assertEqual(type(var1), type(var2))\n            self.assertEqual(var1.name, var2.name)\n            if var1.is_discrete:\n                self.assertEqual(var1.values, var2.values)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/data/tests/test_owpivot.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "1e09a7bd4",
                "new_commit_hash": "502af2709",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/data/tests/test_owpivot.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/data/tests/test_owpivot.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 278,
                            "length": 6
                        },
                        "new_range": {
                            "start": 278,
                            "length": 14
                        },
                        "section": "class TestOWPivot(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_only_metas_table(self):",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertTrue(self.widget.Warning.no_variables.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Warning.no_variables.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_empty_table(self):",
                                "type": "context"
                            },
                            {
                                "content": "        data = self.heart_disease[:, :0]",
                                "type": "context"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, data)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owpivot.py",
                            "type": "class",
                            "name": "TestOWPivot",
                            "content": "class TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n\n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 21,
                            "end_lineno": 305,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owpivot.py",
                            "type": "method",
                            "name": "TestOWPivot.test_only_metas_table",
                            "content": "class TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n\n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 281,
                            "end_lineno": 287,
                            "parent": {
                                "file_name": "Orange/widgets/data/tests/test_owpivot.py",
                                "type": "class",
                                "name": "TestOWPivot",
                                "content": "class TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n\n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 21,
                                "end_lineno": 305,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "918508b33a9c50779e9f4b78ba465fce298f5f72^",
    "new_commit_hash": "918508b33a9c50779e9f4b78ba465fce298f5f72",
    "commit_message": "Pivot: Handle metas only",
    "commit_date": "2021-07-15T09:14:20+02:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "918508b33a9c50779e9f4b78ba465fce298f5f72",
    "test_file_codes": [
        "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring,unsubscriptable-object\nimport unittest\nfrom unittest.mock import patch\nfrom pickle import loads, dumps\n\nimport numpy as np\n\nfrom AnyQt.QtCore import Qt, QPoint\nfrom AnyQt.QtTest import QTest\n\nfrom Orange.data import (Table, Domain, ContinuousVariable as Cv,\n                         StringVariable as sv, DiscreteVariable as Dv,\n                         TimeVariable as Tv)\nfrom Orange.widgets.data.owpivot import (OWPivot, Pivot,\n                                         AggregationFunctionsEnum)\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import simulate\n\n\nclass TestOWPivot(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPivot)\n        self.agg_checkboxes = self.widget.aggregation_checkboxes\n        self.assertGreater(len(self.agg_checkboxes), 0)\n        self.iris = Table(\"iris\")\n        self.heart_disease = Table(\"heart_disease\")\n        self.zoo = Table(\"zoo\")\n\n    def test_comboboxes(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        controls = self.widget.controls\n        name = self.heart_disease.domain.class_var.name\n        self.assertEqual(controls.row_feature.currentText(), name)\n        self.assertEqual(controls.col_feature.currentText(), \"(Same as rows)\")\n        self.assertEqual(controls.val_feature.currentText(), \"age\")\n\n        self.assertEqual(len(controls.row_feature.model()), 15)\n        self.assertEqual(len(controls.col_feature.model()), 11)\n        self.assertEqual(len(controls.val_feature.model()), 17)\n\n        domain = self.heart_disease.domain\n        for var in domain.variables + domain.metas:\n            self.assertIn(var, controls.val_feature.model())\n            if var.is_continuous:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertNotIn(var, controls.col_feature.model())\n            elif var.is_discrete:\n                self.assertIn(var, controls.row_feature.model())\n                self.assertIn(var, controls.col_feature.model())\n\n    def test_feature_combinations(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_run_through_all(self.widget.controls.row_feature)\n        simulate.combobox_run_through_all(self.widget.controls.col_feature)\n        simulate.combobox_run_through_all(self.widget.controls.val_feature)\n\n    def test_output_grouped_data(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        names = [\"iris\", \"(count)\", \"sepal length (sum)\", \"sepal width (sum)\",\n                 \"petal length (sum)\", \"petal width (sum)\"]\n        self.assertListEqual(names, [a.name for a in grouped.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.grouped_data))\n\n    def test_output_grouped_data_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        data = Table(domain, X)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.agg_checkboxes[Pivot.Functions.Mean.value].click()\n        grouped = self.get_output(self.widget.Outputs.grouped_data)\n        str_grouped = \"[[a, 2, 1987-06-06],\\n [b, 2, 1976-05-03]]\"\n        self.assertEqual(str(grouped), str_grouped)\n\n    def test_output_filtered_data(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.filtered_data))\n\n    def test_output_pivot_table(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[0].name)\n        table = self.get_output(self.widget.Outputs.pivot_table)\n        names = [\"iris\", \"Aggregate\", \"Iris-setosa\",\n                 \"Iris-versicolor\", \"Iris-virginica\"]\n        self.assertListEqual(names, [a.name for a in table.domain.variables])\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertIsNone(self.get_output(self.widget.Outputs.pivot_table))\n\n    def test_pivot_table_cont_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        self.assertTrue(self.widget.Warning.no_col_feature.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.no_col_feature.is_shown())\n\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n    def test_pivot_table_disc_row(self):\n        for cb in self.agg_checkboxes[1:]:\n            cb.click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n\n        self.send_signal(self.widget.Inputs.data, self.zoo)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.zoo.domain.metas[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.zoo.domain.attributes[0].name)\n\n    def test_aggregations(self):\n        # agg: Count, feature: Continuous\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Sum, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Majority.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Continuous\n        self.agg_checkboxes[Pivot.Sum.value].click()\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: Discrete\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.class_var.name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        \"(None)\")\n        self.assertTrue(self.widget.Warning.cannot_aggregate.is_shown())\n        # agg: Count, Majority, feature: None, row: Continuous\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.cannot_aggregate.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot._initialize\",\n           return_value=(None, None))\n    def test_group_table_created_once(self, initialize):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        initialize.assert_called_with(set([Pivot.Count]),\n                                      self.iris.domain.attributes[0])\n        initialize.reset_mock()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        initialize.assert_not_called()\n\n    def test_saved_workflow(self):\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        simulate.combobox_activate_item(self.widget.controls.row_feature,\n                                        self.iris.domain.attributes[0].name)\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        self.iris.domain.class_var.name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.iris.domain.attributes[1].name)\n        self.widget.table_view.set_selection(set([(11, 0), (11, 1), (12, 0),\n                                                  (12, 1), (13, 0), (13, 1),\n                                                  (14, 0), (14, 1)]))\n        self.widget.table_view.selection_changed.emit()\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n\n        settings = self.widget.settingsHandler.pack_data(self.widget)\n        w = self.create_widget(self.widget.__class__, stored_settings=settings)\n        self.send_signal(w.Inputs.data, self.iris, widget=w)\n        output = self.get_output(self.widget.Outputs.filtered_data)\n        self.assertEqual(output.X.shape, (20, 4))\n        self.assertSetEqual(self.widget.selection, w.selection)\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n\n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n        self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\n\n        # column\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(340, 40))\n        self.assertSetEqual({(0, 1), (3, 1), (1, 1), (2, 1)},\n                            view.get_selection())\n\n        # group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(155, 75))\n        self.assertSetEqual({(0, 1), (1, 0), (0, 0), (1, 1)},\n                            view.get_selection())\n\n        # all\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(400, 198))\n        self.assertSetEqual({(0, 1), (0, 0), (3, 0), (3, 1), (2, 1), (2, 0),\n                             (1, 0), (1, 1)}, view.get_selection())\n\n    def test_send_report(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.report_button.click()\n        self.send_signal(self.widget.Inputs.data, None)\n        self.widget.report_button.click()\n\n    def test_renaming_warning(self):\n        data = Table('iris')\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertFalse(self.widget.Warning.renamed_vars.is_shown())\n\n    @patch(\"Orange.widgets.data.owpivot.OWPivot.MAX_VALUES\", 2)\n    def test_max_values(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(self.widget.Warning.too_many_values.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.too_many_values.is_shown())\n\n    def test_table_values(self):\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        domain = self.heart_disease.domain\n        self.agg_checkboxes[Pivot.Functions.Majority.value].click()\n        simulate.combobox_activate_item(self.widget.controls.col_feature,\n                                        domain[\"gender\"].name)\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        domain[\"thal\"].name)\n\n        model = self.widget.table_view.model()\n        self.assertEqual(model.data(model.index(2, 3)), \"72.0\")\n        self.assertEqual(model.data(model.index(3, 3)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 3)), \"25.0\")\n        self.assertEqual(model.data(model.index(5, 3)), \"reversable defect\")\n        self.assertEqual(model.data(model.index(2, 4)), \"92.0\")\n        self.assertEqual(model.data(model.index(3, 4)), \"normal\")\n        self.assertEqual(model.data(model.index(4, 4)), \"114.0\")\n        self.assertEqual(model.data(model.index(5, 4)), \"reversable defect\")\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n\n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n\n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n\n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        domain = Domain([], zoo_domain.class_vars, metas=zoo_domain.metas)\n        data = self.zoo.transform(domain)\n        self.send_signal(self.widget.Inputs.data, data)\n\n\nclass TestAggregationFunctionsEnum(unittest.TestCase):\n    def test_pickle(self):\n        self.assertIs(AggregationFunctionsEnum.Sum,\n                      loads(dumps(AggregationFunctionsEnum.Sum)))\n\n    def test_sort(self):\n        af = AggregationFunctionsEnum\n        self.assertEqual(sorted([af.Sum, af.Min]), sorted([af.Min, af.Sum]))\n\n\nclass TestPivot(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")),\n                         Dv(\"d2\", (\"c\", \"d\", \"e\")), Cv(\"c1\")])\n        X = np.array([[0, 0, 1], [0, 1, 2], [0, np.nan, 3], [0, 0, 4],\n                      [1, 0, 5], [1, 0, 6], [1, 1, np.nan], [1, 2, 7],\n                      [np.nan, 0, 8]])\n        cls.table = Table(domain, X)\n\n        domain = Domain([Cv(\"c0\"), Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")],\n                        Dv(\"cls\", (\"a\", \"b\")), [sv(\"m1\"), sv(\"m2\")])\n        X = np.array([[np.nan, 0, 1, 0, 2], [np.nan, 1, 2, np.nan, 3],\n                      [np.nan, 0, 3, 1, np.nan]])\n        M = np.array([[\"aa\", \"dd\"], [\"bb\", \"ee\"], [\"cc\", \"\"]], dtype=object)\n        cls.table1 = Table(domain, X, np.array([0, 0, 1]), M)\n\n    def test_group_table(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(group_tab, Table(Domain(domain[:2] + atts), X))\n\n    def test_group_table_time_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1e9], [0, 1e8], [1, 2e8], [1, np.nan]])\n        table = Table(domain, X)\n        pivot = Pivot(table, Pivot.Functions, domain[0], val_var=domain[1])\n        str_grouped = \\\n            \"[[a, 2, 2, a, 2, 1.1e+09, 1987-06-06, 1973-03-03, \" \\\n            \"2001-09-09, 1973-03-03, 1987-06-06, 2.025e+17],\\n \" \\\n            \"[b, 2, 2, b, 1, 2e+08, 1976-05-03, 1976-05-03, \" \\\n            \"1976-05-03, 1976-05-03, 1976-05-03, 0]]\"\n        self.assertEqual(str(pivot.group_table), str_grouped)\n\n    def test_group_table_metas(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n        table.metas = table.metas.astype(object)\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Count.func\",\n           side_effect=Pivot.Count.func)\n    @patch(\"Orange.widgets.data.owpivot.Pivot.Sum.func\",\n           side_effect=Pivot.Sum.func)\n    def test_group_table_use_cached(self, count_func, sum_func):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count, Pivot.Sum], domain[0], domain[1])\n        group_tab = pivot.group_table\n        count_func.reset_mock()\n        sum_func.reset_mock()\n\n        pivot.update_group_table(Pivot.Functions)\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        self.assert_table_equal(pivot.group_table,\n                                Table(Domain(domain[:2] + atts), X))\n\n        pivot.update_group_table([Pivot.Count, Pivot.Sum])\n        count_func.assert_not_called()\n        sum_func.assert_not_called()\n        self.assert_table_equal(pivot.group_table, group_tab)\n\n    def test_group_table_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0])\n        group_tab = pivot.group_table\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"),\n                Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n                Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        domain = Domain(domain[:1] + atts)\n        X = np.array([[0, 4, 4, 0, 3, 0, 4, 10, 2.5, 1, 4, 1, 2.5, 1.25],\n                      [1, 4, 4, 1, 4, 0, 3, 18, 6, 5, 7, 5, 6, 2 / 3]],\n                     dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(domain[:1] + atts), X))\n\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[0])\n        group_tab_same_vars = pivot.group_table\n        self.assert_table_equal(group_tab, group_tab_same_vars)\n\n    def test_group_table_no_col_var_metas(self):\n        for var in self.table1.domain.metas:\n            self.assertRaises(TypeError, Pivot, self.table1, var)\n\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Cv(\"c1\"),\n                         Dv(\"d2\", (\"a\", \"b\")), Cv(\"c2\")])\n        X = np.array([[0, 1, 0, 2], [1, 2, np.nan, 3], [0, 3, 1, np.nan]])\n        table = Table(domain, X).transform(\n            Domain(domain.attributes[:2], metas=domain.attributes[2:]))\n\n        pivot = Pivot(table, Pivot.Functions, table.domain[-1])\n        group_tab = pivot.group_table\n        atts = (table.domain[-1], Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"), Cv(\"d2 (count defined)\"),\n                Dv(\"d2 (majority)\", [\"a\", \"b\"]), Cv(\"c2 (count defined)\"),\n                Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"), Cv(\"c2 (min)\"), Cv(\"c2 (max)\"),\n                Cv(\"c2 (mode)\"), Cv(\"c2 (median)\"), Cv(\"c2 (var)\"))\n        X = np.array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1,\n                       1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 0],\n                      [1, 1, 1, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 0, np.nan,\n                       np.nan, np.nan, np.nan, np.nan, np.nan]], dtype=float)\n        self.assert_table_equal(group_tab, Table(Domain(atts), X))\n\n    def test_group_table_update(self):\n        domain = self.table.domain\n        atts = (Cv(\"(count)\"), Cv(\"d1 (count defined)\"),\n                Dv(\"d1 (majority)\", [\"a\", \"b\"]),\n                Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"c\", \"d\", \"e\"]),\n                Cv(\"c1 (count defined)\"), Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"),\n                Cv(\"c1 (min)\"), Cv(\"c1 (max)\"), Cv(\"c1 (mode)\"),\n                Cv(\"c1 (median)\"), Cv(\"c1 (var)\"))\n        X = np.array(\n            [[0, 0, 2, 2, 0, 2, 0, 2, 5, 2.5, 1, 4, 1, 2.5, 2.25],\n             [0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],\n             [0, 2, 0, 0, np.nan, 0, np.nan, 0, 0, np.nan,\n              np.nan, np.nan, np.nan, np.nan, np.nan],\n             [1, 0, 2, 2, 1, 2, 0, 2, 11, 5.5, 5, 6, 5, 5.5, 0.25],\n             [1, 1, 1, 1, 1, 1, 1, 0, 0, np.nan, np.nan,\n              np.nan, np.nan, np.nan, np.nan],\n             [1, 2, 1, 1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 0]])\n        table = Table(Domain(domain[:2] + atts), X)\n\n        agg = [Pivot.Functions.Count, Pivot.Functions.Sum]\n        pivot = Pivot(self.table, agg, domain[0], domain[1])\n        group_tab = pivot.group_table\n        pivot.update_group_table(Pivot.Functions)\n        self.assert_table_equal(pivot.group_table, table)\n        pivot.update_group_table(agg)\n        self.assert_table_equal(group_tab, pivot.group_table)\n\n    def test_group_table_1(self):\n        var = self.table1.domain.variables[1]\n        domain = Domain(\n            [var, Cv(\"(count)\"), Cv(\"c0 (count defined)\"), Cv(\"c0 (sum)\"),\n             Cv(\"c0 (mean)\"), Cv(\"c0 (min)\"), Cv(\"c0 (max)\"), Cv(\"c0 (mode)\"),\n             Cv(\"c0 (median)\"), Cv(\"c0 (var)\"), Cv(\"d1 (count defined)\"),\n             Dv(\"d1 (majority)\", [\"a\", \"b\"]), Cv(\"c1 (count defined)\"),\n             Cv(\"c1 (sum)\"), Cv(\"c1 (mean)\"), Cv(\"c1 (min)\"), Cv(\"c1 (max)\"),\n             Cv(\"c1 (mode)\"), Cv(\"c1 (median)\"), Cv(\"c1 (var)\"),\n             Cv(\"d2 (count defined)\"), Dv(\"d2 (majority)\", [\"a\", \"b\"]),\n             Cv(\"c2 (count defined)\"), Cv(\"c2 (sum)\"), Cv(\"c2 (mean)\"),\n             Cv(\"c2 (min)\"), Cv(\"c2 (max)\"), Cv(\"c2 (mode)\"),\n             Cv(\"c2 (median)\"), Cv(\"c2 (var)\"), Cv(\"cls (count defined)\"),\n             Dv(\"cls (majority)\", [\"a\", \"b\"]), Cv(\"m1 (count defined)\"),\n             Cv(\"m2 (count defined)\")])\n        X = np.array([[0, 2, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 2, 0, 2, 4, 2, 1, 3, 1, 2, 1,\n                       2, 0, 1, 2, 2, 2, 2, 2, 2, 0, 2, 0, 2, 1],\n                      [1, 1, 0, 0, np.nan, np.nan, np.nan, np.nan,\n                       np.nan, np.nan, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0,\n                       np.nan, 1, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1, 1]])\n        pivot = Pivot(self.table1, Pivot.Functions, var)\n        group_tab = pivot.group_table\n        self.assert_table_equal(group_tab, Table(domain, X))\n\n    def test_group_sparse_data(self):\n        var = self.table1.domain.variables[1]\n        dense = Pivot(self.table1, Pivot.Functions, var)\n        sparse_data = self.table1.to_sparse()\n        var = sparse_data.domain.variables[1]\n        sparse = Pivot(sparse_data, Pivot.Functions, var)\n        self.assert_table_equal(dense.group_table, sparse.group_table)\n\n    def test_pivot(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1], domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                                 \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0],\n                      [0, 1, 2, 1, 0],\n                      [0, 2, 5, 2, 0],\n                      [0, 3, 2.5, 2, np.nan],\n                      [0, 4, 1, 2, np.nan],\n                      [0, 5, 4, 2, np.nan],\n                      [0, 6, 1, 2, np.nan],\n                      [0, 7, 2.5, 2, np.nan],\n                      [0, 8, 2.25, 0, np.nan],\n                      [1, 0, 2, 1, 1],\n                      [1, 1, 2, 0, 1],\n                      [1, 2, 11, 0, 7],\n                      [1, 3, 5.5, np.nan, 7],\n                      [1, 4, 5, np.nan, 7],\n                      [1, 5, 6, np.nan, 7],\n                      [1, 6, 5, np.nan, 7],\n                      [1, 7, 5.5, np.nan, 7],\n                      [1, 8, 0.25, np.nan, 0]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_total(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count, Pivot.Functions.Sum],\n                      domain[0], domain[1], domain[2])\n\n        atts = (Dv(domain[0].name, [\"Total\"]),\n                Dv(\"Aggregate\", [\"Count\", \"Sum\"]), Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 4, 2, 1], [0, 1, 16, 2, 7]])\n        table = Table(Domain(atts), X)\n\n        self.assert_table_equal(pivot.pivot_total_h, table)\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[3], [7], [4], [18]]))\n        self.assert_table_equal(pivot.pivot_total_v, table)\n\n        table = Table(Domain((Cv(\"Total\"),)), np.array([[7], [25]]))\n        self.assert_table_equal(pivot.pivot_total, table)\n\n    def test_pivot_no_col_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], None, domain[2])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\",\n                   [\"Count\", \"Count defined\", \"Sum\", \"Mean\",\n                    \"Min\", \"Max\", \"Mode\", \"Median\", \"Var\"]),\n                Cv(\"a\"), Cv(\"b\"))\n        X = np.array([[0, 0, 4, 0],\n                      [0, 1, 4, 0],\n                      [0, 2, 10, 0],\n                      [0, 3, 2.5, np.nan],\n                      [0, 4, 1, np.nan],\n                      [0, 5, 4, np.nan],\n                      [0, 6, 1, np.nan],\n                      [0, 7, 2.5, np.nan],\n                      [0, 8, 1.25, np.nan],\n                      [1, 0, 0, 4],\n                      [1, 1, 0, 3],\n                      [1, 2, 0, 18],\n                      [1, 3, np.nan, 6],\n                      [1, 4, np.nan, 5],\n                      [1, 5, np.nan, 7],\n                      [1, 6, np.nan, 5],\n                      [1, 7, np.nan, 6],\n                      [1, 8, np.nan, 2 / 3]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_no_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, Pivot.Functions, domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (Dv(\"Aggregate\", [\"Count\"]),\n                Cv(\"c\"), Cv(\"d\"), Cv(\"e\"))\n        X = np.array([[0, 0, 2, 1, 0], [1, 0, 2, 1, 1]])\n        self.assert_table_equal(pivot_tab, Table(Domain(domain[:1] + atts), X))\n\n    def test_pivot_disc_val_var(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Count_defined, Pivot.Majority],\n                      domain[2], domain[0], domain[1])\n        pivot_tab = pivot.pivot_table\n        atts = (domain[2], Dv(\"Aggregate\", [\"Count defined\", \"Majority\"]),\n                Dv(\"a\", [\"0.0\", \"1.0\", \"c\", \"d\"]),\n                Dv(\"b\", [\"0.0\", \"1.0\", \"c\", \"e\"]))\n        X = np.array([[1, 0, 1, 0],\n                      [1, 1, 2, np.nan],\n                      [2, 0, 1, 0],\n                      [2, 1, 3, np.nan],\n                      [3, 0, 0, 0],\n                      [3, 1, np.nan, np.nan],\n                      [4, 0, 1, 0],\n                      [4, 1, 2, np.nan],\n                      [5, 0, 0, 1],\n                      [5, 1, np.nan, 2],\n                      [6, 0, 0, 1],\n                      [6, 1, np.nan, 2],\n                      [7, 0, 0, 1],\n                      [7, 1, np.nan, 3],\n                      [8, 0, 0, 0],\n                      [8, 1, np.nan, np.nan]])\n        self.assert_table_equal(pivot_tab, Table(Domain(atts), X))\n\n    def test_pivot_time_val_var(self):\n        domain = Domain([Dv(\"d1\", (\"a\", \"b\")), Dv(\"d2\", (\"c\", \"d\")),\n                         Tv(\"t1\", have_date=1)])\n        X = np.array([[0, 1, 1e9], [0, 0, 1e8], [1, 0, 2e8], [1, 1, np.nan]])\n        table = Table(domain, X)\n\n        # Min\n        pivot = Pivot(table, [Pivot.Min],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Min, Max\n        pivot = Pivot(table, [Pivot.Min, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Min\", \"Max\"]),\n                Tv(\"c\", have_date=1), Tv(\"d\", have_date=1))\n        X = np.array([[0, 0, 1e8, 1e9],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 2e8, np.nan],\n                      [1, 1, 2e8, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Sum\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Sum],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Sum\"]),\n                Cv(\"c\"), Cv(\"d\"))\n        X = np.array([[0, 0, 1, 1],\n                      [0, 1, 1e8, 1e9],\n                      [1, 0, 1, 0],\n                      [1, 1, 2e8, 0]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n        # Count defined, Max\n        pivot = Pivot(table, [Pivot.Count_defined, Pivot.Max],\n                      domain[0], domain[1], domain[2])\n        atts = (domain[0], Dv(\"Aggregate\", [\"Count defined\", \"Max\"]),\n                Dv(\"c\", [\"1.0\", \"1973-03-03\", \"1976-05-03\"]),\n                Dv(\"d\", [\"0.0\", \"1.0\", \"2001-09-09\"]))\n        X = np.array([[0, 0, 0, 1],\n                      [0, 1, 1, 2],\n                      [1, 0, 0, 0],\n                      [1, 1, 2, np.nan]])\n        self.assert_table_equal(pivot.pivot_table, Table(Domain(atts), X))\n\n    def test_pivot_attr_combinations(self):\n        domain = self.table1.domain\n        for var1, var2, var3 in ((domain[1], domain[3], domain[5]),  # d d d\n                                 (domain[1], domain[3], domain[4]),  # d d c\n                                 (domain[1], domain[3], domain[-1]),  # d d s\n                                 (domain[2], domain[3], domain[5]),  # c d d\n                                 (domain[2], domain[3], domain[4]),  # c d c\n                                 (domain[2], domain[3], domain[-1])):  # c d s\n            pivot = Pivot(self.table1, Pivot.Functions, var1, var2, var3)\n            pivot_tab = pivot.pivot_table\n            self.assertGreaterEqual(pivot_tab.X.shape[0], 4)\n            self.assertGreaterEqual(pivot_tab.X.shape[1], 4)\n        for var1, var2 in ((domain[1], domain[2]),  # d c\n                           (domain[1], domain[-2]),  # d s\n                           (domain[2], domain[4]),  # c\n                           (domain[-1], domain[1])):  # s\n            self.assertRaises(TypeError, Pivot, self.table1, var1, var2)\n\n    def test_pivot_update(self):\n        domain = self.table.domain\n        pivot = Pivot(self.table, [Pivot.Functions.Count], domain[0],\n                      domain[1], domain[2])\n        pivot_tab1 = pivot.pivot_table\n        pivot.update_pivot_table(domain[1])\n        pivot.update_pivot_table(domain[2])\n        self.assert_table_equal(pivot_tab1, pivot.pivot_table)\n\n    def test_pivot_data_subset(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var\n        pivot = Pivot(data[:100], Pivot.Functions, cls_var, None, cls_var)\n        atts = (cls_var, Dv(\"Aggregate\", [\"Count\", \"Count defined\", \"Majority\"]),\n                Dv(\"Iris-setosa\", [\"0.0\", \"50.0\", \"Iris-setosa\"]),\n                Dv(\"Iris-versicolor\", [\"0.0\", \"50.0\", \"Iris-versicolor\"]))\n        domain = Domain(atts)\n        self.assert_domain_equal(domain, pivot.pivot_table.domain)\n\n    def test_pivot_renaming_domain(self):\n        data = Table(\"iris\")\n        cls_var = data.domain.class_var.copy(name='Aggregate')\n        data.domain = Domain(data.domain.attributes, (cls_var,))\n        pivot = Pivot(data, [Pivot.Functions.Sum], cls_var, None, None)\n\n        renamed_var = data.domain.class_var.copy(name='Aggregate (1)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n        renamed_var = data.domain.class_var.copy(name='Aggregate (2)')\n        self.assertTrue(renamed_var in pivot.pivot_table.domain)\n\n    def assert_table_equal(self, table1, table2):\n        self.assert_domain_equal(table1.domain, table2.domain)\n        np.testing.assert_array_equal(table1.X, table2.X)\n        np.testing.assert_array_equal(table1.Y, table2.Y)\n        np.testing.assert_array_equal(table1.metas, table2.metas)\n\n    def assert_domain_equal(self, domain1, domain2):\n        for var1, var2 in zip(domain1.variables + domain1.metas,\n                              domain2.variables + domain2.metas):\n            self.assertEqual(type(var1), type(var2))\n            self.assertEqual(var1.name, var2.name)\n            if var1.is_discrete:\n                self.assertEqual(var1.values, var2.values)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Orange installation attempts...\nAttempting installation with Python 3.7...\nrunning build_ext\nCompiling Orange/classification/_tree_scorers.pyx because it changed.\nCompiling Orange/data/_contingency.pyx because it changed.\nCompiling Orange/data/_io.pyx because it changed.\nCompiling Orange/data/_valuecount.pyx because it changed.\nCompiling Orange/data/_variable.pyx because it changed.\nCompiling Orange/distance/_distance.pyx because it changed.\nCompiling Orange/preprocess/_discretize.pyx because it changed.\nCompiling Orange/preprocess/_relieff.pyx because it changed.\nCompiling Orange/projection/_som.pyx because it changed.\n[1/9] Cythonizing Orange/classification/_tree_scorers.pyx\n[2/9] Cythonizing Orange/data/_contingency.pyx\n[3/9] Cythonizing Orange/data/_io.pyx\n[4/9] Cythonizing Orange/data/_valuecount.pyx\n[5/9] Cythonizing Orange/data/_variable.pyx\n[6/9] Cythonizing Orange/distance/_distance.pyx\n[7/9] Cythonizing Orange/preprocess/_discretize.pyx\n[8/9] Cythonizing Orange/preprocess/_relieff.pyx\n[9/9] Cythonizing Orange/projection/_som.pyx\nbuilding 'Orange.classification._tree_scorers' extension\ncreating build\ncreating build/temp.linux-x86_64-3.7\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/classification/_tree_scorers.c -o build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o\ncreating build/lib.linux-x86_64-3.7\ncreating build/lib.linux-x86_64-3.7/Orange\ncreating build/lib.linux-x86_64-3.7/Orange/classification\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_contingency.c -o build/temp.linux-x86_64-3.7/Orange/data/_contingency.o\ncreating build/lib.linux-x86_64-3.7/Orange/data\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_contingency.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_io.c -o build/temp.linux-x86_64-3.7/Orange/data/_io.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_io.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_valuecount.c -o build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_variable.c -o build/temp.linux-x86_64-3.7/Orange/data/_variable.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_variable.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/distance/_distance.c -o build/temp.linux-x86_64-3.7/Orange/distance/_distance.o\ncreating build/lib.linux-x86_64-3.7/Orange/distance\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/distance/_distance.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/preprocess/_discretize.c -o build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o\ncreating build/lib.linux-x86_64-3.7/Orange/preprocess\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/preprocess/_relieff.cpp -o build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/projection/_som.c -o build/temp.linux-x86_64-3.7/Orange/projection/_som.o\ncreating build/lib.linux-x86_64-3.7/Orange/projection\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/projection/_som.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._simple_tree' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/classification/_simple_tree.c -o build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/widgets/utils/_grid_density.cpp -o build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o\ncreating build/lib.linux-x86_64-3.7/Orange/widgets\ncreating build/lib.linux-x86_64-3.7/Orange/widgets/utils\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so -> Orange/distance\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so -> Orange/projection\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so -> Orange/widgets/utils\nrunning develop\nrunning egg_info\ncreating Orange3.egg-info\nwriting Orange3.egg-info/PKG-INFO\nwriting dependency_links to Orange3.egg-info/dependency_links.txt\nwriting entry points to Orange3.egg-info/entry_points.txt\nwriting requirements to Orange3.egg-info/requires.txt\nwriting top-level names to Orange3.egg-info/top_level.txt\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest template 'MANIFEST.in'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so -> Orange/distance\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so -> Orange/projection\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so -> Orange/widgets/utils\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.30.0.dev0+918508b to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72\nProcessing dependencies for Orange3==3.30.0.dev0+918508b\nSearching for XlsxWriter==3.2.0\nBest match: XlsxWriter 3.2.0\nAdding XlsxWriter 3.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for xlrd==2.0.1\nBest match: xlrd 2.0.1\nAdding xlrd 2.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for setuptools==59.8.0\nBest match: setuptools 59.8.0\nAdding setuptools 59.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for serverfiles==0.3.1\nBest match: serverfiles 0.3.1\nAdding serverfiles 0.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for scipy==1.7.3\nBest match: scipy 1.7.3\nAdding scipy 1.7.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for scikit-learn==1.0.2\nBest match: scikit-learn 1.0.2\nAdding scikit-learn 1.0.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for requests==2.31.0\nBest match: requests 2.31.0\nAdding requests 2.31.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyYAML==6.0.1\nBest match: PyYAML 6.0.1\nAdding PyYAML 6.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for python-louvain==0.16\nBest match: python-louvain 0.16\nAdding python-louvain 0.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pyqtgraph==0.12.4\nBest match: pyqtgraph 0.12.4\nAdding pyqtgraph 0.12.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pip==24.0\nBest match: pip 24.0\nAdding pip 24.0 to easy-install.pth file\nInstalling pip script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pandas==1.3.5\nBest match: pandas 1.3.5\nAdding pandas 1.3.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for orange-widget-base==4.22.0\nBest match: orange-widget-base 4.22.0\nAdding orange-widget-base 4.22.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for orange-canvas-core==0.1.35\nBest match: orange-canvas-core 0.1.35\nAdding orange-canvas-core 0.1.35 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for openpyxl==3.1.3\nBest match: openpyxl 3.1.3\nAdding openpyxl 3.1.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for openTSNE==1.0.1\nBest match: openTSNE 1.0.1\nAdding openTSNE 1.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for numpy==1.17.5\nBest match: numpy 1.17.5\nAdding numpy 1.17.5 to easy-install.pth file\nInstalling f2py script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for networkx==2.6.3\nBest match: networkx 2.6.3\nAdding networkx 2.6.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for matplotlib==3.5.3\nBest match: matplotlib 3.5.3\nAdding matplotlib 3.5.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for keyrings.alt==4.2.0\nBest match: keyrings.alt 4.2.0\nAdding keyrings.alt 4.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for keyring==24.1.1\nBest match: keyring 24.1.1\nAdding keyring 24.1.1 to easy-install.pth file\nInstalling keyring script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for joblib==1.3.2\nBest match: joblib 1.3.2\nAdding joblib 1.3.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for httpx==0.16.1\nBest match: httpx 0.16.1\nAdding httpx 0.16.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for chardet==5.2.0\nBest match: chardet 5.2.0\nAdding chardet 5.2.0 to easy-install.pth file\nInstalling chardetect script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for Bottleneck==1.4.0\nBest match: Bottleneck 1.4.0\nAdding Bottleneck 1.4.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for baycomp==1.0.3\nBest match: baycomp 1.0.3\nAdding baycomp 1.0.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyQtWebEngine==5.15.6\nBest match: PyQtWebEngine 5.15.6\nAdding PyQtWebEngine 5.15.6 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyQt5==5.15.10\nBest match: PyQt5 5.15.10\nAdding PyQt5 5.15.10 to easy-install.pth file\nInstalling pylupdate5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pyrcc5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pyuic5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for AnyQt==0.2.0\nBest match: AnyQt 0.2.0\nAdding AnyQt 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for threadpoolctl==3.1.0\nBest match: threadpoolctl 3.1.0\nAdding threadpoolctl 3.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for charset-normalizer==3.4.1\nBest match: charset-normalizer 3.4.1\nAdding charset-normalizer 3.4.1 to easy-install.pth file\nInstalling normalizer script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for urllib3==2.0.7\nBest match: urllib3 2.0.7\nAdding urllib3 2.0.7 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for idna==3.10\nBest match: idna 3.10\nAdding idna 3.10 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for certifi==2024.12.14\nBest match: certifi 2024.12.14\nAdding certifi 2024.12.14 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pytz==2024.2\nBest match: pytz 2024.2\nAdding pytz 2024.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for python-dateutil==2.9.0.post0\nBest match: python-dateutil 2.9.0.post0\nAdding python-dateutil 2.9.0.post0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for typing-extensions==4.7.1\nBest match: typing-extensions 4.7.1\nAdding typing-extensions 4.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for docutils==0.20.1\nBest match: docutils 0.20.1\nAdding docutils 0.20.1 to easy-install.pth file\nInstalling docutils script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for commonmark==0.9.1\nBest match: commonmark 0.9.1\nAdding commonmark 0.9.1 to easy-install.pth file\nInstalling cmark script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for requests-cache==1.1.1\nBest match: requests-cache 1.1.1\nAdding requests-cache 1.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for importlib-metadata==6.7.0\nBest match: importlib-metadata 6.7.0\nAdding importlib-metadata 6.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for packaging==24.0\nBest match: packaging 24.0\nAdding packaging 24.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for dictdiffer==0.9.0\nBest match: dictdiffer 0.9.0\nAdding dictdiffer 0.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for qasync==0.25.0\nBest match: qasync 0.25.0\nAdding qasync 0.25.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for et-xmlfile==1.1.0\nBest match: et-xmlfile 1.1.0\nAdding et-xmlfile 1.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for fonttools==4.38.0\nBest match: fonttools 4.38.0\nAdding fonttools 4.38.0 to easy-install.pth file\nInstalling fonttools script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pyparsing==3.1.4\nBest match: pyparsing 3.1.4\nAdding pyparsing 3.1.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for Pillow==9.5.0\nBest match: Pillow 9.5.0\nAdding Pillow 9.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for cycler==0.11.0\nBest match: cycler 0.11.0\nAdding cycler 0.11.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for kiwisolver==1.4.5\nBest match: kiwisolver 1.4.5\nAdding kiwisolver 1.4.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for jaraco.classes==3.2.3\nBest match: jaraco.classes 3.2.3\nAdding jaraco.classes 3.2.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for importlib-resources==5.12.0\nBest match: importlib-resources 5.12.0\nAdding importlib-resources 5.12.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for jeepney==0.8.0\nBest match: jeepney 0.8.0\nAdding jeepney 0.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for SecretStorage==3.3.3\nBest match: SecretStorage 3.3.3\nAdding SecretStorage 3.3.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for httpcore==0.12.3\nBest match: httpcore 0.12.3\nAdding httpcore 0.12.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for sniffio==1.3.1\nBest match: sniffio 1.3.1\nAdding sniffio 1.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for rfc3986==1.5.0\nBest match: rfc3986 1.5.0\nAdding rfc3986 1.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyQt5-sip==12.13.0\nBest match: PyQt5-sip 12.13.0\nAdding PyQt5-sip 12.13.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyQtWebEngine-Qt5==5.15.16\nBest match: PyQtWebEngine-Qt5 5.15.16\nAdding PyQtWebEngine-Qt5 5.15.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for PyQt5-Qt5==5.15.16\nBest match: PyQt5-Qt5 5.15.16\nAdding PyQt5-Qt5 5.15.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for six==1.17.0\nBest match: six 1.17.0\nAdding six 1.17.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for url-normalize==1.4.3\nBest match: url-normalize 1.4.3\nAdding url-normalize 1.4.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for cattrs==23.1.2\nBest match: cattrs 23.1.2\nAdding cattrs 23.1.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for attrs==24.2.0\nBest match: attrs 24.2.0\nAdding attrs 24.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for platformdirs==4.0.0\nBest match: platformdirs 4.0.0\nAdding platformdirs 4.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for zipp==3.15.0\nBest match: zipp 3.15.0\nAdding zipp 3.15.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for more-itertools==9.1.0\nBest match: more-itertools 9.1.0\nAdding more-itertools 9.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for cryptography==44.0.0\nBest match: cryptography 44.0.0\nAdding cryptography 44.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for h11==0.14.0\nBest match: h11 0.14.0\nAdding h11 0.14.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for exceptiongroup==1.2.2\nBest match: exceptiongroup 1.2.2\nAdding exceptiongroup 1.2.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for cffi==1.15.1\nBest match: cffi 1.15.1\nAdding cffi 1.15.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nSearching for pycparser==2.21\nBest match: pycparser 2.21\nAdding pycparser 2.21 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.30.0.dev0+918508b\nVerifying Orange installation...\n✅ Orange installation successful!\nSuccessfully installed orange using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 18 packages in 116ms\nPrepared 18 packages in 2ms\nInstalled 18 packages in 109ms\n + cython==0.29.37\n + exceptiongroup==1.2.2\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + numpy==1.17.5\n + packaging==24.0\n + pluggy==1.2.0\n + pyqt5==5.15.10\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.13.0\n + pyqtwebengine==5.15.6\n + pyqtwebengine-qt5==5.15.16\n + pytest==7.4.4\n + setuptools==59.8.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + wheel==0.42.0\n + zipp==3.15.0\nResolved 53 packages in 281ms\nInstalled 47 packages in 624ms\n + baycomp==1.0.3\n + bottleneck==1.4.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + cryptography==44.0.0\n + cycler==0.11.0\n + et-xmlfile==1.1.0\n + fonttools==4.38.0\n + h11==0.14.0\n + httpcore==0.12.3\n + httpx==0.16.1\n + idna==3.10\n + importlib-resources==5.12.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + joblib==1.3.2\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + kiwisolver==1.4.5\n + matplotlib==3.5.3\n + more-itertools==9.1.0\n + networkx==2.6.3\n + openpyxl==3.1.3\n + opentsne==1.0.1\n + pandas==1.3.5\n + pillow==9.5.0\n + pip==24.0\n + pycparser==2.21\n + pyparsing==3.1.4\n + python-dateutil==2.9.0.post0\n + python-louvain==0.16\n + pytz==2024.2\n + pyyaml==6.0.1\n + requests==2.31.0\n + rfc3986==1.5.0\n + scikit-learn==1.0.2\n + scipy==1.7.3\n + secretstorage==3.3.3\n + serverfiles==0.3.1\n + six==1.17.0\n + sniffio==1.3.1\n + threadpoolctl==3.1.0\n + urllib3==2.0.7\n + xlrd==2.0.1\n + xlsxwriter==3.2.0\nResolved 39 packages in 133ms\nInstalled 13 packages in 133ms\n + anyqt==0.2.0\n + attrs==24.2.0\n + cattrs==23.1.2\n + commonmark==0.9.1\n + dictdiffer==0.9.0\n + docutils==0.20.1\n + orange-canvas-core==0.1.35\n + orange-widget-base==4.22.0\n + platformdirs==4.0.0\n + pyqtgraph==0.12.4\n + qasync==0.25.0\n + requests-cache==1.1.1\n + url-normalize==1.4.3\nResolved 2 packages in 9ms\nInstalled 2 packages in 125ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nResolved 19 packages in 34ms\nInstalled 5 packages in 1.22s\n + catboost==1.2.7\n + graphviz==0.20.1\n + plotly==5.18.0\n + tenacity==8.2.3\n + xgboost==1.6.2\nAudited 2 packages in 45ms\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_2find_threshold_entropy’:\nOrange/classification/_tree_scorers.c:3413:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3413 |   __pyx_t_7 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_6find_threshold_MSE’:\nOrange/classification/_tree_scorers.c:4869:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4869 |   __pyx_t_1 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_8find_binarization_MSE’:\nOrange/classification/_tree_scorers.c:5615:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5615 |           for (__pyx_t_19 = 0; __pyx_t_19 < __pyx_t_18; __pyx_t_19+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5736:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5736 |           __pyx_t_20 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5742:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5742 |           __pyx_t_20 = (((__pyx_v_N - __pyx_v_left) >= __pyx_v_min_leaf) != 0);\n      |                                                     ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_14compute_predictions_csr’:\nOrange/classification/_tree_scorers.c:7202:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7202 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_16compute_predictions_csc’:\nOrange/classification/_tree_scorers.c:7857:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7857 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7917:143: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7917 |               __pyx_t_15 = (((*((__pyx_t_5numpy_int32_t *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_14 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                               ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/data/_io.c: In function ‘__pyx_pf_6Orange_4data_3_io_sparse_read_float.constprop’:\nOrange/data/_io.c:4120:43: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4120 |           __pyx_v_value = ((__pyx_v_value * __pyx_v_decs) + (__pyx_v_c & 0xf));\n      |                            ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/data/_io.c:4318:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4318 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 223, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:683:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  683 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1590:7: note: ‘__pyx_v_attr_index’ was declared here\n 1590 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:3183:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3183 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:774:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:790:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_5update_hex’:\nOrange/projection/_som.c:3863:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3863 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3862:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3862 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_8update_sparse.constprop’:\nOrange/projection/_som.c:4934:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4934 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4933:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4933 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_7get_winners_sparse’:\nOrange/projection/_som.c:4732:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4732 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4307:7: note: ‘__pyx_v_win_y’ was declared here\n 4307 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4721:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4721 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4306:7: note: ‘__pyx_v_win_x’ was declared here\n 4306 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_10update_sparse_hex.constprop’:\nOrange/projection/_som.c:5682:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5682 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5681:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5681 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:3184:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3184 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_22, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_23, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2887:7: note: ‘__pyx_v_win_y’ was declared here\n 2887 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3173:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3173 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_23, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2886:7: note: ‘__pyx_v_win_x’ was declared here\n 2886 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3357:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3357 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/classification/_simple_tree.c: In function ‘gain_ratio_c’:\nOrange/classification/_simple_tree.c:63:9: warning: implicit declaration of function ‘qsort_r’; did you mean ‘qsort’? [-Wimplicit-function-declaration]\n   63 |         qsort_r(base, nel, size, compar, thunk)\n      |         ^~~~~~~\nOrange/classification/_simple_tree.c:147:9: note: in expansion of macro ‘QSORT_R_FUNC’\n  147 |         QSORT_R_FUNC(examples, size, sizeof(struct Example), (void*) &compar_attr, compar_examples);\n      |         ^~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/setuptools/command/easy_install.py:159: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n  EasyInstallDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\nwarning: no files found matching 'README*' under directory 'Orange'\nwarning: no files found matching 'LICENSE' under directory 'Orange'\nwarning: no files found matching '*.basket' under directory 'Orange/datasets'\nwarning: no files found matching '*.js' under directory 'Orange/widgets'\nwarning: no files found matching '*.css' under directory 'Orange/widgets'\nwarning: no files found matching '*.html' under directory 'Orange/widgets'\nwarning: no files found matching 'COPYING'\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72\ncollected 45 items\n\nr2e_tests/test_1.py sss..................F.......................        [100%]\n\n=================================== FAILURES ===================================\n_______________________ TestOWPivot.test_select_by_click _______________________\n\nself = <r2e_tests.test_1.TestOWPivot testMethod=test_select_by_click>\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n    \n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n>       self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\nE       AssertionError: Items in the second set but not the first:\nE       (3, 1)\nE       (2, 1)\n\nr2e_tests/test_1.py:221: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPivot::test_aggregations\nPASSED r2e_tests/test_1.py::TestOWPivot::test_comboboxes\nPASSED r2e_tests/test_1.py::TestOWPivot::test_empty_table\nPASSED r2e_tests/test_1.py::TestOWPivot::test_feature_combinations\nPASSED r2e_tests/test_1.py::TestOWPivot::test_group_table_created_once\nPASSED r2e_tests/test_1.py::TestOWPivot::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPivot::test_max_values\nPASSED r2e_tests/test_1.py::TestOWPivot::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPivot::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPivot::test_only_metas_table\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_filtered_data\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_grouped_data\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_grouped_data_time_var\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_pivot_table\nPASSED r2e_tests/test_1.py::TestOWPivot::test_pivot_table_cont_row\nPASSED r2e_tests/test_1.py::TestOWPivot::test_pivot_table_disc_row\nPASSED r2e_tests/test_1.py::TestOWPivot::test_renaming_warning\nPASSED r2e_tests/test_1.py::TestOWPivot::test_saved_workflow\nPASSED r2e_tests/test_1.py::TestOWPivot::test_send_report\nPASSED r2e_tests/test_1.py::TestOWPivot::test_table_values\nPASSED r2e_tests/test_1.py::TestAggregationFunctionsEnum::test_pickle\nPASSED r2e_tests/test_1.py::TestAggregationFunctionsEnum::test_sort\nPASSED r2e_tests/test_1.py::TestPivot::test_group_sparse_data\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_1\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_metas\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_no_col_var\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_no_col_var_metas\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_time_var\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_update\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_use_cached\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_attr_combinations\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_data_subset\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_disc_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_no_col_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_no_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_renaming_domain\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_time_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_total\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_update\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWPivot::test_select_by_click - AssertionErro...\n=================== 1 failed, 41 passed, 3 skipped in 7.50s ====================\nWidgets left: 0    Max widgets: 2552 \n\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_918508b33a9c50779e9f4b78ba465fce298f5f72\ncollected 45 items\n\nr2e_tests/test_1.py sss..F......F........F.......................        [100%]\n\n=================================== FAILURES ===================================\n_________________________ TestOWPivot.test_empty_table _________________________\n\nself = <r2e_tests.test_1.TestOWPivot testMethod=test_empty_table>\n\n    def test_empty_table(self):\n        data = self.heart_disease[:, :0]\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Warning.no_variables.is_shown())\n    \n        data = self.heart_disease\n        self.send_signal(self.widget.Inputs.data, data)\n    \n        zoo_domain = self.zoo.domain\n        data = self.zoo.transform(Domain([], metas=zoo_domain.metas))\n>       self.send_signal(self.widget.Inputs.data, data)\n\nr2e_tests/test_1.py:301: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:446: in send_signal\n    widget=widget, wait=wait)\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:471: in send_signals\n    self._send_signal(widget, input, value, *args)\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:490: in _send_signal\n    notify_input_helper(input, widget, value, *args, **kwargs)\n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/functools.py:840: in wrapper\n    return dispatch(args[0].__class__)(*args, **kw)\n.venv/lib/python3.7/site-packages/orangewidget/utils/signals.py:735: in set_input_helper\n    handler(*args)\n.venv/lib/python3.7/site-packages/orangewidget/utils/signals.py:208: in summarize_wrapper\n    method(widget, value)\nOrange/widgets/utils/sql.py:31: in new_f\n    return f(widget, data, *args, **kwargs)\nOrange/widgets/data/owpivot.py:906: in set_data\n    self.init_attr_values()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <Orange.widgets.data.owpivot.OWPivot object at 0x70ea4fae8a50>\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n>               if domain.variables[0] in model else model[2]\nE           IndexError: tuple index out of range\n\nOrange/widgets/data/owpivot.py:925: IndexError\n______________________ TestOWPivot.test_only_metas_table _______________________\n\nself = <r2e_tests.test_1.TestOWPivot testMethod=test_only_metas_table>\n\n    def test_only_metas_table(self):\n        self.send_signal(self.widget.Inputs.data, self.zoo[:, 17:])\n        self.assertTrue(self.widget.Warning.no_variables.is_shown())\n    \n        data = self.zoo.transform(Domain([], metas=self.zoo.domain.attributes))\n>       self.send_signal(self.widget.Inputs.data, data)\n\nr2e_tests/test_1.py:286: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:446: in send_signal\n    widget=widget, wait=wait)\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:471: in send_signals\n    self._send_signal(widget, input, value, *args)\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:490: in _send_signal\n    notify_input_helper(input, widget, value, *args, **kwargs)\n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/functools.py:840: in wrapper\n    return dispatch(args[0].__class__)(*args, **kw)\n.venv/lib/python3.7/site-packages/orangewidget/utils/signals.py:735: in set_input_helper\n    handler(*args)\n.venv/lib/python3.7/site-packages/orangewidget/utils/signals.py:208: in summarize_wrapper\n    method(widget, value)\nOrange/widgets/utils/sql.py:31: in new_f\n    return f(widget, data, *args, **kwargs)\nOrange/widgets/data/owpivot.py:906: in set_data\n    self.init_attr_values()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <Orange.widgets.data.owpivot.OWPivot object at 0x70ea4f5404b0>\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n>               if domain.variables[0] in model else model[2]\nE           IndexError: tuple index out of range\n\nOrange/widgets/data/owpivot.py:925: IndexError\n_______________________ TestOWPivot.test_select_by_click _______________________\n\nself = <r2e_tests.test_1.TestOWPivot testMethod=test_select_by_click>\n\n    def test_select_by_click(self):\n        view = self.widget.table_view\n        self.send_signal(self.widget.Inputs.data, self.heart_disease)\n        self.agg_checkboxes[Pivot.Functions.Sum.value].click()\n        simulate.combobox_activate_item(self.widget.controls.val_feature,\n                                        self.heart_disease.domain[0].name)\n    \n        # column in a group\n        QTest.mouseClick(view.viewport(), Qt.LeftButton, pos=QPoint(208, 154))\n>       self.assertSetEqual({(3, 0), (2, 0)}, view.get_selection())\nE       AssertionError: Items in the second set but not the first:\nE       (3, 1)\nE       (2, 1)\n\nr2e_tests/test_1.py:221: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPivot::test_aggregations\nPASSED r2e_tests/test_1.py::TestOWPivot::test_comboboxes\nPASSED r2e_tests/test_1.py::TestOWPivot::test_feature_combinations\nPASSED r2e_tests/test_1.py::TestOWPivot::test_group_table_created_once\nPASSED r2e_tests/test_1.py::TestOWPivot::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPivot::test_max_values\nPASSED r2e_tests/test_1.py::TestOWPivot::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPivot::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_filtered_data\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_grouped_data\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_grouped_data_time_var\nPASSED r2e_tests/test_1.py::TestOWPivot::test_output_pivot_table\nPASSED r2e_tests/test_1.py::TestOWPivot::test_pivot_table_cont_row\nPASSED r2e_tests/test_1.py::TestOWPivot::test_pivot_table_disc_row\nPASSED r2e_tests/test_1.py::TestOWPivot::test_renaming_warning\nPASSED r2e_tests/test_1.py::TestOWPivot::test_saved_workflow\nPASSED r2e_tests/test_1.py::TestOWPivot::test_send_report\nPASSED r2e_tests/test_1.py::TestOWPivot::test_table_values\nPASSED r2e_tests/test_1.py::TestAggregationFunctionsEnum::test_pickle\nPASSED r2e_tests/test_1.py::TestAggregationFunctionsEnum::test_sort\nPASSED r2e_tests/test_1.py::TestPivot::test_group_sparse_data\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_1\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_metas\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_no_col_var\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_no_col_var_metas\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_time_var\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_update\nPASSED r2e_tests/test_1.py::TestPivot::test_group_table_use_cached\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_attr_combinations\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_data_subset\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_disc_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_no_col_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_no_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_renaming_domain\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_time_val_var\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_total\nPASSED r2e_tests/test_1.py::TestPivot::test_pivot_update\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWPivot::test_empty_table - IndexError: tuple...\nFAILED r2e_tests/test_1.py::TestOWPivot::test_only_metas_table - IndexError: ...\nFAILED r2e_tests/test_1.py::TestOWPivot::test_select_by_click - AssertionErro...\n=================== 3 failed, 39 passed, 3 skipped in 5.50s ====================\nWidgets left: 0    Max widgets: 2552 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/data/owpivot.py Orange/widgets/data/tests/test_owpivot.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Pivot Widget crashes with IndexError when handling tables with only meta attributes

**Description:**
When a data table containing only meta attributes is provided to the Pivot widget, it results in an unexpected crash due to an `IndexError`. This behavior prevents users from utilizing meta-only datasets effectively.

**Example Code:**
```python
def test_pivot_with_only_metas():
    zoo_data = load_zoo_dataset()
    metas_only = zoo_data[:, 17:]  # Select only meta attributes
    pivot_widget.set_data(metas_only)
    # Expected: Pivot widget handles metas-only table without errors
```

**Expected Behavior:**
The Pivot widget should gracefully handle tables that include only meta attributes. This could involve disabling certain features that rely on regular attributes or displaying an informative warning to the user, ensuring the widget remains stable.

**Actual Behavior:**
An `IndexError` occurs with the message `'tuple index out of range'`, causing the Pivot widget to crash when attempting to access variables that are not present in the meta-only dataset.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/data/owpivot.py
# pylint: disable=missing-docstring
from typing import Iterable, Set
from collections import defaultdict
from itertools import product, chain

import numpy as np
from scipy import sparse as sp

from AnyQt.QtCore import (Qt, QSize, QItemSelection, QItemSelectionModel,
                          pyqtSignal)
from AnyQt.QtGui import QStandardItem, QColor, QStandardItemModel
from AnyQt.QtWidgets import (QTableView, QSizePolicy, QHeaderView,
                             QStyledItemDelegate, QCheckBox, QFrame, QWidget,
                             QGridLayout)

from Orange.data import (Table, DiscreteVariable, Variable, Domain,
                         ContinuousVariable, TimeVariable)
from Orange.data.domain import filter_visible
from Orange.data.util import get_unique_names_duplicates, get_unique_names
from Orange.data.filter import FilterContinuous, FilterDiscrete, Values
from Orange.statistics.util import (nanmin, nanmax, nanunique, nansum, nanvar,
                                    nanmean, nanmedian, nanmode, bincount)
from Orange.util import Enum
from Orange.widgets import gui
from Orange.widgets.settings import (Setting, ContextSetting,
                                     DomainContextHandler)
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Input, Output, Msg


BorderRole = next(gui.OrangeUserRole)
BorderColorRole = next(gui.OrangeUserRole)


class AggregationFunctionsEnum(Enum):
    (Count, Count_defined, Sum, Mean, Min, Max,
     Mode, Median, Var, Majority) = range(10)

    def __init__(self, *_, **__):
        super().__init__()
        self.func = None

    @property
    def value(self):
        return self._value_

    def __call__(self, *args):
        return self.func(args)  # pylint: disable=not-callable

    def __str__(self):
        return self._name_.replace("_", " ")

    def __gt__(self, other):
        return self._value_ > other.value


class Pivot:
    Functions = AggregationFunctionsEnum
    (Count, Count_defined, Sum, Mean, Min, Max,
     Mode, Median, Var, Majority) = Functions

    AutonomousFunctions = (Count,)
    AnyVarFunctions = (Count_defined,)
    ContVarFunctions = (Sum, Mean, Min, Max, Mode, Median, Var)
    DiscVarFunctions = (Majority,)
    TimeVarFunctions = (Mean, Min, Max, Mode, Median)
    FloatFunctions = (Count, Count_defined, Sum, Var)

    class Tables:
        table = None  # type: Table
        total_h = None  # type: Table
        total_v = None  # type: Table
        total = None  # type: Table

        def __call__(self):
            return self.table, self.total_h, self.total_v, self.total

    def __init__(self, table: Table, agg_funs: Iterable[Functions],
                 row_var: Variable, col_var: Variable = None,
                 val_var: Variable = None):
        self._group_tables = self.Tables()
        self._pivot_tables = self.Tables()
        self._table = table
        self._row_var = row_var
        self._col_var = col_var if col_var else row_var
        self.renamed = []

        if not table:
            return
        if not self._row_var.is_primitive():
            raise TypeError("Row variable should be DiscreteVariable"
                            " or ContinuousVariable")
        if self._col_var and not self._col_var.is_discrete:
            raise TypeError("Column variable should be DiscreteVariable")

        self._row_var_col = table.get_column_view(row_var)[0].astype(np.float)
        self._col_var_col = table.get_column_view(self._col_var)[0].astype(np.float)
        self._row_var_groups = nanunique(self._row_var_col)
        self._col_var_groups = nanunique(self._col_var_col)

        self._total_var = DiscreteVariable("Total", values=("total", ))
        self._current_agg_functions = sorted(agg_funs)
        self._indepen_agg_done = {}  # type: Dict[Functions, int]
        self._depen_agg_done = {}  # type: Dict[Functions, Dict[Variable, int]]

        self._initialize(agg_funs, val_var)

    @property
    def group_table(self) -> Table:
        table = self._group_tables.table
        if not table or len(table) == 0:
            return None
        indices = [0, 1] if not self.single_var_grouping else [0]
        for f in self._current_agg_functions:
            if f in self._indepen_agg_done:
                indices.append(self._indepen_agg_done[f])
        for v in self._table.domain.variables + self._table.domain.metas:
            for f in self._current_agg_functions:
                if f in self._depen_agg_done and v in self._depen_agg_done[f]:
                    indices.append(self._depen_agg_done[f][v])
        return table[:, indices]

    @property
    def pivot_table(self) -> Table:
        return self._pivot_tables.table

    @property
    def pivot_total_h(self) -> Table:
        return self._pivot_tables.total_h

    @property
    def pivot_total_v(self) -> Table:
        return self._pivot_tables.total_v

    @property
    def pivot_total(self) -> Table:
        return self._pivot_tables.total

    @property
    def pivot_tables(self) -> Table:
        return self._pivot_tables()

    @property
    def single_var_grouping(self) -> bool:
        return self._row_var is self._col_var

    def update_group_table(self, agg_funs: Iterable[Functions],
                           val_var: Variable = None):
        if not self._group_tables:
            return
        self._current_agg_functions = sorted(agg_funs)
        agg_funs = set(self._indepen_agg_done.keys()) | \
            set(self._depen_agg_done.keys()) | set(agg_funs)
        self._initialize(sorted(agg_funs), val_var)

    def _initialize(self, agg_funs, val_var):
        var_indep_funs, var_dep_funs = self.__group_aggregations(agg_funs)
        self._create_group_tables(var_indep_funs, var_dep_funs)
        self.__reference_aggregations(var_indep_funs, var_dep_funs)
        self._create_pivot_tables(val_var)

    def __group_aggregations(self, agg_funs):
        auto_funcs = self.AutonomousFunctions
        var_indep_funs = [fun for fun in agg_funs if fun in auto_funcs]
        var_dep_funs = []
        attrs = self._table.domain.variables + self._table.domain.metas
        prod = product(filter_visible(attrs),
                       [fun for fun in agg_funs if fun not in auto_funcs])
        for var, fun in prod:
            if self.__include_aggregation(fun, var):
                var_dep_funs.append((var, fun))
        return var_indep_funs, var_dep_funs

    def __include_aggregation(self, fun, var):
        return fun in self.ContVarFunctions and var.is_continuous or \
               fun in self.DiscVarFunctions and var.is_discrete or \
               fun in self.AnyVarFunctions

    def __reference_aggregations(self, var_indep_funs, var_dep_funs):
        self._indepen_agg_done = {}
        self._depen_agg_done = defaultdict(dict)
        i = 1 - int(bool(self.single_var_grouping))
        for i, fun in enumerate(var_indep_funs, i + 1):
            self._indepen_agg_done[fun] = i
        for j, (var, fun) in enumerate(var_dep_funs, i + 1):
            self._depen_agg_done[fun].update({var: j})

    def _create_group_tables(self, var_indep_funs, var_dep_funs):
        attrs = [ContinuousVariable(f"({str(fun).lower()})")
                 for fun in var_indep_funs]
        for var, fun in var_dep_funs:
            name = f"{var.name} ({str(fun).lower()})"
            if fun in self.DiscVarFunctions:
                attrs.append(DiscreteVariable(name, var.values))
            else:
                if isinstance(var, TimeVariable) and \
                        fun in self.TimeVarFunctions:
                    attrs.append(TimeVariable(name, have_date=var.have_date,
                                              have_time=var.have_time))
                else:
                    attrs.append(ContinuousVariable(name))
        args = (var_indep_funs, var_dep_funs, attrs)
        for t, var in (("table", None), ("total_h", self._col_var),
                       ("total_v", self._row_var), ("total", self._total_var)):
            setattr(self._group_tables, t, self.__get_group_table(var, *args))

    def __get_group_table(self, var, var_indep_funs, var_dep_funs, attrs):
        if var is self._total_var:
            group_tab = self._group_tables.total
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._total_var]
            combs = np.array([[0]])
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._row_var_col)) &
                                     (~np.isnan(self._col_var_col)))[0]]
        elif var is self._row_var or self.single_var_grouping:
            group_tab = self._group_tables.total_v
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._row_var]
            combs = self._row_var_groups[:, None]
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._col_var_col)) &
                                     (self._row_var_col == x[0]))[0]]
        elif var is self._col_var:
            group_tab = self._group_tables.total_h
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._col_var]
            combs = self._col_var_groups[:, None]
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._row_var_col)) &
                                     (self._col_var_col == x[0]))[0]]
        else:
            group_tab = self._group_tables.table
            offset = 0
            leading_vars = [self._row_var, self._col_var]
            combs = np.array(list(product(self._row_var_groups,
                                          self._col_var_groups)))
            sub_table_getter = lambda x: \
                self._table[np.where((self._row_var_col == x[0])
                                     & (self._col_var_col == x[1]))[0]]

        if not combs.shape[0]:
            return None

        n = len(var_indep_funs) + len(var_dep_funs)
        X = np.zeros((len(combs), n), dtype=float)
        for i, comb in enumerate(combs):
            sub_table = sub_table_getter(comb)
            j = -1
            for j, fun in enumerate(var_indep_funs):
                if fun in self._indepen_agg_done:
                    # TODO - optimize - after this line is executed,
                    # the whole column is already set
                    X[:, j] = group_tab.X[:, self._indepen_agg_done[fun] - offset]
                else:
                    X[i, j] = fun(sub_table)
            for k, (v, fun) in enumerate(var_dep_funs, j + 1):
                if fun in self._depen_agg_done:
                    X[:, k] = group_tab.X[:, self._depen_agg_done[fun][v] - offset]
                else:
                    X[i, k] = fun(sub_table.get_column_view(v)[0])

        #rename leading vars (seems the easiest) if needed
        current = [var.name for var in attrs]
        uniq_leading_vars = []
        for v in leading_vars:
            uniq = get_unique_names(current, v.name)
            if uniq != v.name:
                self.renamed.append(v.name)
                v = v.copy(name=uniq)
            uniq_leading_vars.append(v)
            current.append(uniq)

        return Table(Domain(uniq_leading_vars + attrs), np.hstack((combs, X)))

    def update_pivot_table(self, val_var: Variable):
        self._create_pivot_tables(val_var)

    def _create_pivot_tables(self, val_var):
        if not self._group_tables.table:
            self._pivot_tables = self.Tables()
            return

        agg_funs = [fun for fun in self._current_agg_functions
                    if fun in self.AutonomousFunctions
                    or val_var and self.__include_aggregation(fun, val_var)]
        X, X_h, X_v, X_t = self.__get_pivot_tab_x(val_var, agg_funs)
        dom, dom_h, dom_v, dom_t = self.__get_pivot_tab_domain(
            val_var, X, X_h, X_v, X_t, agg_funs)
        for t, d, x in (("table", dom, X), ("total_h", dom_h, X_h),
                        ("total_v", dom_v, X_v), ("total", dom_t, X_t)):
            setattr(self._pivot_tables, t, Table(d, x))

    # pylint: disable=invalid-name
    def __get_pivot_tab_domain(self, val_var, X, X_h, X_v, X_t, agg_funs):
        def map_values(index, _X):
            values = np.unique(_X[:, index])
            values = np.delete(values, np.where(values == "nan")[0])
            for j, value in enumerate(values):
                _X[:, index][_X[:, index] == value] = j
            return values

        create_time_var = \
            isinstance(val_var, TimeVariable) and \
            all(fun in self.TimeVarFunctions for fun in agg_funs)
        create_cont_var = \
            not val_var or val_var.is_continuous and \
            (not isinstance(val_var, TimeVariable) or
             all(fun in self.FloatFunctions for fun in agg_funs))

        vals = np.array(self._col_var.values)[self._col_var_groups.astype(int)]
        if create_time_var:
            kwargs = {"have_date": val_var.have_date,
                      "have_time": val_var.have_time}
            attrs = [[TimeVariable(f"{v}", **kwargs) for v in vals]] * 2
            attrs.extend([[TimeVariable("Total", **kwargs)]] * 2)
        elif create_cont_var:
            attrs = [[ContinuousVariable(f"{v}", 1) for v in vals]] * 2
            attrs.extend([[ContinuousVariable("Total", 1)]] * 2)
        else:
            attrs = []
            for x in (X, X_h):
                attrs.append([DiscreteVariable(f"{v}", map_values(i, x))
                              for i, v in enumerate(vals, 2)])
            for x in (X_v, X_t):
                attrs.append([DiscreteVariable("Total", map_values(0, x))])
        row_var_h = DiscreteVariable(self._row_var.name, values=["Total"])
        aggr_attr = DiscreteVariable('Aggregate', [str(f) for f in agg_funs])

        same_row_col = self._col_var is self._row_var

        extra_vars = [self._row_var, aggr_attr]
        uniq_a = get_unique_names_duplicates([v.name for v in extra_vars]
                                             + [atr.name for atr in attrs[0]])
        for (idx, var), u in zip(enumerate(chain(extra_vars, attrs[0])), uniq_a):
            if var.name == u:
                continue
            if idx == 0:
                self.renamed.append(self._row_var.name)
                self._row_var = self._row_var.copy(name=u)
                if same_row_col:
                    self._col_var = self._row_var
                row_var_h = row_var_h.copy(name=u)
            elif idx == 1:
                self.renamed.append(aggr_attr.name)
                aggr_attr = aggr_attr.copy(name=u)
            else:
                self.renamed.append(var.name)
                attrs[0][idx-2] = var.copy(name=u)
                attrs[1][idx-2] = var.copy(name=u)

        if same_row_col:
            vals = tuple(v.name for v in attrs[0])
            self._row_var.make(self._row_var.name, values=vals)
            vals = tuple(v.name for v in attrs[2])
            row_var_h.make(row_var_h.name, vals)

        return (Domain([self._row_var, aggr_attr] + attrs[0]),
                Domain([row_var_h, aggr_attr] + attrs[1]),
                Domain(attrs[2]), Domain(attrs[3]))

    def __get_pivot_tab_x(self, val_var, agg_funs):
        gt = self._group_tables
        n_fun = len(agg_funs)
        n_rows, n_cols = len(self._row_var_groups), len(self._col_var_groups)
        is_float_type = not val_var or val_var.is_continuous
        if isinstance(val_var, TimeVariable):
            is_float_type = \
                all(fun in self.TimeVarFunctions for fun in agg_funs) or \
                all(fun in self.FloatFunctions for fun in agg_funs)
        kwargs = {"fill_value": np.nan, "dtype": float} if is_float_type \
            else {"fill_value": "", "dtype": object}
        X = np.full((n_rows * n_fun, 2 + n_cols), **kwargs)
        X_h = np.full((n_fun, 2 + n_cols), **kwargs)
        X_v = np.full((n_rows * n_fun, 1), **kwargs)
        X_t = np.full((n_fun, 1), **kwargs)
        for i, fun in enumerate(agg_funs):
            args = (val_var, fun, is_float_type)
            X[i::n_fun, 2:] = self.__rows_for_function(n_rows, n_cols, *args)
            X[i::n_fun, :2] = np.array([[row_val, agg_funs.index(fun)] for
                                        row_val in self._row_var_groups])
            X_h[i, :2] = 0, agg_funs.index(fun)
            X_h[i, 2:] = self.__total_for_function(gt.total_h, *args)
            X_v[i::n_fun, 0] = self.__total_for_function(gt.total_v, *args)
            X_t[i] = self.__total_for_function(gt.total, *args)
        return X, X_h, X_v, X_t

    def __total_for_function(self, group_tab, val_var, fun, is_float_type):
        ref = self._indepen_agg_done.get(fun, None) \
              or self._depen_agg_done[fun][val_var]
        ref -= int(bool(not self.single_var_grouping))
        return self.__check_continuous(val_var, group_tab.X[:, ref],
                                       fun, is_float_type)

    def __rows_for_function(self, n_rows, n_cols, val_var, fun, is_float_type):
        ref = self._indepen_agg_done.get(fun, None) \
              or self._depen_agg_done[fun][val_var]
        column = self._group_tables.table.X[:, ref]
        if self.single_var_grouping:
            rows = np.full((n_rows, n_cols), fun(np.array([]), ), dtype=float)
            rows[np.diag_indices_from(rows)] = column
        else:
            rows = column.reshape(n_rows, n_cols)
        return self.__check_continuous(val_var, rows, fun, is_float_type)

    def __check_continuous(self, val_var, column, fun, is_float_type):
        if val_var and not val_var.is_continuous:
            column = column.astype(str)
            if fun in self.DiscVarFunctions:
                for j, val in enumerate(val_var.values):
                    column[column == str(float(j))] = val
        elif isinstance(val_var, TimeVariable) and not is_float_type:
            shape = column.shape
            column = column.flatten()
            column_ = column.astype(str)
            if fun in self.TimeVarFunctions:
                for i in range(column.shape[0]):
                    if not np.isnan(column[i]):
                        column_[i] = val_var.repr_val(column[i])
            return column_.reshape(shape)
        return column

    @staticmethod
    def count_defined(x):
        if x.shape[0] == 0:
            return 0
        if x.size and np.issubdtype(x.dtype, np.number) and not sp.issparse(x):
            nans = np.isnan(x).sum(axis=0)
        elif sp.issparse(x) and x.size:
            nans = np.bincount(x.nonzero()[1], minlength=x.shape[1])
            x = x.tocsc()
        else:
            x_str = x.astype(str)
            nans = ((x_str == "nan") | (x_str == "")).sum(axis=0) \
                if x.size else np.zeros(x.shape[1])
        return x.shape[0] - nans

    @staticmethod
    def stat(x, f):
        return f(x.astype(np.float), axis=0) if x.shape[0] > 0 else np.nan

    @staticmethod
    def mode(x):
        return Pivot.stat(x, nanmode).mode if x.shape[0] > 0 else np.nan

    @staticmethod
    def majority(x):
        if x.shape[0] == 0:
            return np.nan
        counts = bincount(x)[0]
        return np.argmax(counts) if counts.shape[0] else np.nan

    Count.func = lambda x: len(x[0])
    Count_defined.func = lambda x: Pivot.count_defined(x[0])
    Sum.func = lambda x: nansum(x[0], axis=0) if x[0].shape[0] > 0 else 0
    Mean.func = lambda x: Pivot.stat(x[0], nanmean)
    Min.func = lambda x: Pivot.stat(x[0], nanmin)
    Max.func = lambda x: Pivot.stat(x[0], nanmax)
    Median.func = lambda x: Pivot.stat(x[0], nanmedian)
    Mode.func = lambda x: Pivot.mode(x[0])
    Var.func = lambda x: Pivot.stat(x[0], nanvar)
    Majority.func = lambda x: Pivot.majority(x[0])


class BorderedItemDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        """Overloads `paint` to draw borders"""
        QStyledItemDelegate.paint(self, painter, option, index)
        if index.data(BorderRole):
            painter.save()
            painter.setPen(index.data(BorderColorRole))
            rect = option.rect
            painter.drawLine(rect.topLeft(), rect.topRight())
            painter.restore()


class PivotTableView(QTableView):
    selection_changed = pyqtSignal()

    TOTAL_STRING = "Total"

    def __init__(self):
        super().__init__(editTriggers=QTableView.NoEditTriggers)
        self._n_classesv = None  # number of row_feature values
        self._n_classesh = None  # number of col_feature values
        self._n_agg_func = None  # number of aggregation functions
        self._n_leading_rows = None  # number of leading rows
        self._n_leading_cols = None  # number of leading columns

        self.table_model = QStandardItemModel(self)
        self.setModel(self.table_model)
        self.horizontalHeader().hide()
        self.verticalHeader().hide()
        self.horizontalHeader().setMinimumSectionSize(60)
        self.setShowGrid(False)
        self.setSizePolicy(QSizePolicy.MinimumExpanding,
                           QSizePolicy.MinimumExpanding)
        self.setItemDelegate(BorderedItemDelegate())
        self.pressed.connect(self.__cell_clicked)
        self.clicked.connect(self.__cell_clicked)
        self.entered.connect(self.__cell_entered)
        self.__clicked_cell = None

    @property
    def add_agg_column(self) -> bool:
        return self._n_agg_func > 1

    def __cell_entered(self, model_index):
        if self.__clicked_cell is None:
            return
        index = self.table_model.index
        selection = None
        i_end, j_end = model_index.row(), model_index.column()
        i_start, j_start = self.__clicked_cell
        i_start, i_end = sorted([i_start, i_end])
        j_start, j_end = sorted([j_start, j_end])
        if i_start >= self._n_leading_rows and j_start >= self._n_leading_cols:
            i_start = (i_start - self._n_leading_rows) // self._n_agg_func * \
                self._n_agg_func + self._n_leading_rows
            i_end = (i_end - self._n_leading_rows) // self._n_agg_func * \
                self._n_agg_func + self._n_leading_rows + self._n_agg_func - 1
            start, end = index(i_start, j_start), index(i_end, j_end)
            selection = QItemSelection(start, end)
        if selection is not None:
            self.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)
        self.selection_changed.emit()

    def __cell_clicked(self, model_index):
        i, j = model_index.row(), model_index.column()
        self.__clicked_cell = (i, j)
        m, n = self.table_model.rowCount(), self.table_model.columnCount()
        index = self.table_model.index
        selection = None
        if i > m - self._n_agg_func - 1 and j == n - 1:
            start_index = index(self._n_leading_rows, self._n_leading_cols)
            selection = QItemSelection(start_index, index(m - 1, n - 1))
        elif i == self._n_leading_rows - 1 or i > m - self._n_agg_func - 1:
            start_index = index(self._n_leading_rows, j)
            selection = QItemSelection(start_index, index(m - 1, j))
        elif j in (self._n_leading_cols - 1, n - 1, 1):
            i_start = (i - self._n_leading_rows) // self._n_agg_func * \
                      self._n_agg_func + self._n_leading_rows
            i_end = i_start + self._n_agg_func - 1
            start_index = index(i_start, self._n_leading_cols)
            selection = QItemSelection(start_index, index(i_end, n - 1))
        elif i >= self._n_leading_rows and j >= self._n_leading_cols:
            i_start = (i - self._n_leading_rows) // self._n_agg_func * \
                      self._n_agg_func + self._n_leading_rows
            i_end = i_start + self._n_agg_func - 1
            selection = QItemSelection(index(i_start, j), index(i_end, j))

        if selection is not None:
            self.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def mouseReleaseEvent(self, e):
        super().mouseReleaseEvent(e)
        self.selection_changed.emit()

    def update_table(self, titleh: str, titlev: str, table: Table,
                     table_total_h: Table, table_total_v: Table,
                     table_total: Table):
        self.clear()
        if not table:
            return

        self._initialize(table, table_total_h)
        self._set_headers(titleh, titlev, table)
        self._set_values(table[:, 2:])
        self._set_totals(table_total_h[:, 2:], table_total_v, table_total)
        self._draw_lines()
        self._resize(table)

    def _initialize(self, table, table_total_h):
        self._n_classesv = int(len(table) / len(table_total_h))
        self._n_classesh = table.X.shape[1] - 2
        self._n_agg_func = len(table_total_h)
        self._n_leading_rows = 2
        self._n_leading_cols = 2 + int(len(table_total_h) > 1)

    def _set_headers(self, titleh, titlev, table):
        self.__set_horizontal_title(titleh)
        self.__set_vertical_title(titlev)
        self.__set_flags_title()
        self.__set_horizontal_headers(table)
        self.__set_vertical_headers(table)

    def __set_horizontal_title(self, titleh):
        item = QStandardItem()
        item.setData(titleh, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        self.table_model.setItem(0, self._n_leading_cols, item)
        self.setSpan(0, self._n_leading_cols, 1, self._n_classesh + 3)

    def __set_vertical_title(self, titlev):
        item = QStandardItem()
        item.setData(titlev, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        self.setItemDelegateForColumn(0, gui.VerticalItemDelegate(extend=True))
        self.table_model.setItem(self._n_leading_rows, 0, item)
        row_span = self._n_classesv * self._n_agg_func + 1
        self.setSpan(self._n_leading_rows, 0, row_span, 1)

    def __set_flags_title(self):
        item = self.table_model.item(0, self._n_leading_cols)
        item.setFlags(Qt.NoItemFlags)
        item = self.table_model.item(self._n_leading_rows, 0)
        item.setFlags(Qt.NoItemFlags)
        for i, j in product(range(self._n_leading_rows),
                            range(self._n_leading_cols)):
            item = QStandardItem()
            item.setFlags(Qt.NoItemFlags)
            self.table_model.setItem(i, j, item)

    def __set_horizontal_headers(self, table):
        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]
        if not self.add_agg_column:
            labels[0] = str(table[0, 1])
        for i, label in enumerate(labels, self._n_leading_cols - 1):
            self.table_model.setItem(1, i, self._create_header_item(label))

    def __set_vertical_headers(self, table):
        labels = [(str(row[0]), str(row[1])) for row in table]
        i = self._n_leading_rows - 1
        for i, (l1, l2) in enumerate(labels, self._n_leading_rows):
            l1 = "" if (i - self._n_leading_rows) % self._n_agg_func else l1
            self.table_model.setItem(i, 1, self._create_header_item(l1))
            if self.add_agg_column:
                self.table_model.setItem(i, 2, self._create_header_item(l2))

        if self.add_agg_column:
            labels = [str(row[1]) for row in table[:self._n_agg_func]]
            start = self._n_leading_rows + self._n_agg_func * self._n_classesv
            for j, l2 in enumerate(labels, i + 1):
                l1 = self.TOTAL_STRING if j == start else ""
                self.table_model.setItem(j, 1, self._create_header_item(l1))
                self.table_model.setItem(j, 2, self._create_header_item(l2))
        else:
            item = self._create_header_item(self.TOTAL_STRING)
            self.table_model.setItem(i + 1, 1, item)

    def _set_values(self, table):
        for i, j in product(range(len(table)), range(len(table[0]))):
            value = table[i, j]
            item = self._create_value_item(str(value))
            self.table_model.setItem(i + self._n_leading_rows,
                                     j + self._n_leading_cols, item)

    def _set_totals(self, table_total_h, table_total_v, table_total):
        def set_total_item(table, get_row, get_col):
            for i, j in product(range(len(table)), range(len(table[0]))):
                item = self._create_header_item(str(table[i, j]))
                self.table_model.setItem(get_row(i), get_col(j), item)

        last_row = self._n_leading_rows + self._n_classesv * self._n_agg_func
        last_col = self._n_leading_cols + self._n_classesh
        set_total_item(table_total_v, lambda x: x + self._n_leading_rows,
                       lambda x: last_col)
        set_total_item(table_total_h, lambda x: x + last_row,
                       lambda x: x + self._n_leading_cols)
        set_total_item(table_total, lambda x: x + last_row, lambda x: last_col)

    def _create_header_item(self, text):
        bold_font = self.table_model.invisibleRootItem().font()
        bold_font.setBold(True)
        item = QStandardItem()
        item.setData(text, Qt.DisplayRole)
        item.setFont(bold_font)
        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
        item.setFlags(Qt.ItemIsEnabled)
        return item

    @staticmethod
    def _create_value_item(text):
        item = QStandardItem()
        item.setData(text, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        return item

    def _draw_lines(self):
        end_col = self._n_leading_cols + self._n_classesh + 1
        total_row = self._n_leading_rows + self._n_classesv * self._n_agg_func
        indices = [(total_row, j) for j in range(1, end_col)]
        for i in range(self._n_classesv):
            inner_row = self._n_agg_func * i + self._n_leading_rows
            inner_indices = [(inner_row, j) for j in range(1, end_col)]
            indices = indices + inner_indices
            if not self.add_agg_column:
                break
        for i, j in indices:
            item = self.table_model.item(i, j)
            item.setData("t", BorderRole)
            item.setData(QColor(160, 160, 160), BorderColorRole)

    def _resize(self, table):
        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]
        if len(' '.join(labels)) < 120:
            self.horizontalHeader().setSectionResizeMode(
                QHeaderView.ResizeToContents)
        else:
            self.horizontalHeader().setDefaultSectionSize(60)

    def get_selection(self) -> Set:
        m, n = self._n_leading_rows, self._n_leading_cols
        return {(ind.row() - m, ind.column() - n)
                for ind in self.selectedIndexes()}

    def set_selection(self, indexes: Set):
        selection = QItemSelection()
        index = self.model().index
        for row, col in indexes:
            sel = index(row + self._n_leading_rows, col + self._n_leading_cols)
            selection.select(sel, sel)
        self.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def clear(self):
        self.table_model.clear()


class OWPivot(OWWidget):
    name = "Pivot Table"
    description = "Reshape data table based on column values."
    icon = "icons/Pivot.svg"
    priority = 1000
    keywords = ["pivot", "group", "aggregate"]

    class Inputs:
        data = Input("Data", Table, default=True)

    class Outputs:
        pivot_table = Output("Pivot Table", Table, default=True)
        filtered_data = Output("Filtered Data", Table)
        grouped_data = Output("Grouped Data", Table)

    class Warning(OWWidget.Warning):
        # TODO - inconsistent for different variable types
        no_col_feature = Msg("Column feature should be selected.")
        cannot_aggregate = Msg("Some aggregations ({}) cannot be computed.")
        renamed_vars = Msg("Some variables have been renamed in some tables"
                           "to avoid duplicates.\n{}")
        too_many_values = Msg("Selected variable has too many values.")
        no_variables = Msg("At least 1 primitive variable is required.")

    settingsHandler = DomainContextHandler()
    row_feature = ContextSetting(None)
    col_feature = ContextSetting(None)
    val_feature = ContextSetting(None)
    sel_agg_functions = Setting(set([Pivot.Count]))
    selection = Setting(set(), schema_only=True)
    auto_commit = Setting(True)

    AGGREGATIONS = (Pivot.Count,
                    Pivot.Count_defined,
                    None,  # separator
                    Pivot.Sum,
                    Pivot.Mean,
                    Pivot.Var,
                    Pivot.Median,
                    2,  # column break
                    Pivot.Mode,
                    Pivot.Min,
                    Pivot.Max,
                    None,
                    Pivot.Majority)

    MAX_VALUES = 100

    def __init__(self):
        super().__init__()
        self.data = None  # type: Table
        self.pivot = None  # type: Pivot
        self.__pending_selection = self.selection  # type: Set
        self._add_control_area_controls()
        self._add_main_area_controls()

    def _add_control_area_controls(self):
        gui.comboBox(gui.vBox(self.controlArea, box="Rows"),
                     self, "row_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),
                     callback=self.__feature_changed,
                     orientation=Qt.Horizontal)
        gui.comboBox(gui.vBox(self.controlArea, box="Columns"),
                     self, "col_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(placeholder="(Same as rows)",
                                       valid_types=DiscreteVariable),
                     callback=self.__feature_changed,
                     orientation=Qt.Horizontal)
        gui.comboBox(gui.vBox(self.controlArea, box="Values"),
                     self, "val_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(placeholder="(None)"),
                     callback=self.__val_feature_changed,
                     orientation=Qt.Horizontal)
        self.__add_aggregation_controls()
        gui.rubber(self.controlArea)
        gui.auto_apply(self.buttonsArea, self, "auto_commit")

    def __add_aggregation_controls(self):
        def new_inbox():
            nonlocal row, col, inbox
            inbox = QWidget()
            layout = QGridLayout()
            inbox.setLayout(layout)
            layout.setContentsMargins(0, 0, 0, 0)
            box.layout().addWidget(inbox)
            row = col = 0

        box = gui.vBox(self.controlArea, "Aggregations")
        row = col = 0
        inbox = None
        new_inbox()
        self.aggregation_checkboxes = []  # for test purposes
        for agg in self.AGGREGATIONS:
            if agg is None:
                line = QFrame()
                line.setFrameShape(QFrame.HLine)
                line.setLineWidth(1)
                line.setFrameShadow(QFrame.Sunken)
                box.layout().addWidget(line)
                new_inbox()
                continue
            elif agg == 2:
                col += 1
                row = 0
                continue
            check_box = QCheckBox(str(agg), inbox)
            check_box.setChecked(agg in self.sel_agg_functions)
            check_box.clicked.connect(lambda *args, a=agg:
                                      self.__aggregation_cb_clicked(a, args[0]))
            inbox.layout().addWidget(check_box, row, col)
            self.aggregation_checkboxes.append(check_box)
            row += 1

    def _add_main_area_controls(self):
        self.table_view = PivotTableView()
        self.table_view.selection_changed.connect(self.__invalidate_filtered)
        self.mainArea.layout().addWidget(self.table_view)

    @property
    def no_col_feature(self):
        return self.col_feature is None and self.row_feature is not None \
            and self.row_feature.is_continuous

    @property
    def skipped_aggs(self):
        def add(fun):
            data, var = self.data, self.val_feature
            return data and not var and fun not in Pivot.AutonomousFunctions \
                or var and var.is_discrete and fun in Pivot.ContVarFunctions \
                or var and var.is_continuous and fun in Pivot.DiscVarFunctions
        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]
        return ", ".join(sorted(skipped))

    @property
    def data_has_primitives(self):
        if not self.data:
            return False
        domain = self.data.domain
        return any(v.is_primitive() for v in domain.variables + domain.metas)

    def __feature_changed(self):
        self.selection = set()
        self.pivot = None
        self.commit()

    def __val_feature_changed(self):
        self.selection = set()
        if self.no_col_feature or not self.pivot:
            return
        self.pivot.update_pivot_table(self.val_feature)
        self.commit()

    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):
        self.selection = set()
        if checked:
            self.sel_agg_functions.add(agg_fun)
        else:
            self.sel_agg_functions.remove(agg_fun)
        if self.no_col_feature or not self.pivot or not self.data:
            return
        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)
        self.commit()

    def __invalidate_filtered(self):
        self.selection = self.table_view.get_selection()
        self.commit()

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.selection = set()
        self.data = data
        self.pivot = None
        self.check_data()
        self.init_attr_values()
        if self.data_has_primitives:
            self.openContext(self.data)
        self.unconditional_commit()

    def check_data(self):
        self.clear_messages()

    def init_attr_values(self):
        domain = self.data.domain if self.data and len(self.data) else None
        for attr in ("row_feature", "col_feature", "val_feature"):
            getattr(self.controls, attr).model().set_domain(domain)
            setattr(self, attr, None)
        model = self.controls.row_feature.model()
        if model:
            self.row_feature = model[0]
        model = self.controls.val_feature.model()
        if model and len(model) > 2:
            self.val_feature = domain.variables[0] \
                if domain.variables[0] in model else model[2]

    def commit(self):
        def send_outputs(pivot_table, filtered_data, grouped_data):
            if self.data:
                if grouped_data:
                    grouped_data.name = self.data.name
                if pivot_table:
                    pivot_table.name = self.data.name
                if filtered_data:
                    filtered_data.name = self.data.name
            self.Outputs.grouped_data.send(grouped_data)
            self.Outputs.pivot_table.send(pivot_table)
            self.Outputs.filtered_data.send(filtered_data)

        self.Warning.renamed_vars.clear()
        self.Warning.too_many_values.clear()
        self.Warning.cannot_aggregate.clear()
        self.Warning.no_col_feature.clear()

        self.table_view.clear()

        if self.pivot is None:
            if self.data:
                if not self.data_has_primitives:
                    self.Warning.no_variables()
                    send_outputs(None, None, None)
                    return

            if self.no_col_feature:
                self.Warning.no_col_feature()
                send_outputs(None, None, None)
                return

            if self.data:
                col_var = self.col_feature or self.row_feature
                col = self.data.get_column_view(col_var)[0].astype(np.float)
                if len(nanunique(col)) >= self.MAX_VALUES:
                    self.table_view.clear()
                    self.Warning.too_many_values()
                    send_outputs(None, None, None)
                    return

            self.pivot = Pivot(self.data, self.sel_agg_functions,
                               self.row_feature,
                               self.col_feature, self.val_feature)

        if self.skipped_aggs:
            self.Warning.cannot_aggregate(self.skipped_aggs)
        self._update_graph()

        send_outputs(self.pivot.pivot_table,
                     self.get_filtered_data(),
                     self.pivot.group_table)

        if self.pivot.renamed:
            self.Warning.renamed_vars(self.pivot.renamed)

    def _update_graph(self):
        if self.pivot.pivot_table:
            col_feature = self.col_feature or self.row_feature
            self.table_view.update_table(col_feature.name,
                                         self.row_feature.name,
                                         *self.pivot.pivot_tables)
            selection = self.__pending_selection or self.selection
            self.table_view.set_selection(selection)
            self.selection = self.table_view.get_selection()
            self.__pending_selection = set()

    def get_filtered_data(self):
        if not self.data or not self.selection or not self.pivot.pivot_table:
            return None

        cond = []
        for i, j in self.selection:
            f = []
            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),
                            (self.col_feature, j)]:
                if isinstance(at, DiscreteVariable):
                    f.append(FilterDiscrete(at, [val]))
                elif isinstance(at, ContinuousVariable):
                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))
            cond.append(Values(f))
        return Values(cond, conjunction=False)(self.data)

    @staticmethod
    def sizeHint():
        return QSize(640, 525)

    def send_report(self):
        self.report_items((
            ("Row feature", self.row_feature),
            ("Column feature", self.col_feature),
            ("Value feature", self.val_feature)))
        if self.data and self.val_feature is not None:
            self.report_table("", self.table_view)
        if not self.data:
            self.report_items((("Group by", self.row_feature),))
            self.report_table(self.table_view)


if __name__ == "__main__":
    WidgetPreview(OWPivot).run(set_data=Table("heart_disease"))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Pivot Widget crashes with IndexError when handling tables with only meta attributes

**Description:**
When a data table containing only meta attributes is provided to the Pivot widget, it results in an unexpected crash due to an `IndexError`. This behavior prevents users from utilizing meta-only datasets effectively.

**Example Code:**
```python
def test_pivot_with_only_metas():
    zoo_data = load_zoo_dataset()
    metas_only = zoo_data[:, 17:]  # Select only meta attributes
    pivot_widget.set_data(metas_only)
    # Expected: Pivot widget handles metas-only table without errors
```

**Expected Behavior:**
The Pivot widget should gracefully handle tables that include only meta attributes. This could involve disabling certain features that rely on regular attributes or displaying an informative warning to the user, ensuring the widget remains stable.

**Actual Behavior:**
An `IndexError` occurs with the message `'tuple index out of range'`, causing the Pivot widget to crash when attempting to access variables that are not present in the meta-only dataset.

[/ISSUE]
{
    "TestOWPivot.test_aggregations": "PASSED",
    "TestOWPivot.test_comboboxes": "PASSED",
    "TestOWPivot.test_empty_table": "PASSED",
    "TestOWPivot.test_feature_combinations": "PASSED",
    "TestOWPivot.test_group_table_created_once": "PASSED",
    "TestOWPivot.test_image_export": "PASSED",
    "TestOWPivot.test_max_values": "PASSED",
    "TestOWPivot.test_minimum_size": "PASSED",
    "TestOWPivot.test_msg_base_class": "PASSED",
    "TestOWPivot.test_only_metas_table": "PASSED",
    "TestOWPivot.test_output_filtered_data": "PASSED",
    "TestOWPivot.test_output_grouped_data": "PASSED",
    "TestOWPivot.test_output_grouped_data_time_var": "PASSED",
    "TestOWPivot.test_output_pivot_table": "PASSED",
    "TestOWPivot.test_pivot_table_cont_row": "PASSED",
    "TestOWPivot.test_pivot_table_disc_row": "PASSED",
    "TestOWPivot.test_renaming_warning": "PASSED",
    "TestOWPivot.test_saved_workflow": "PASSED",
    "TestOWPivot.test_send_report": "PASSED",
    "TestOWPivot.test_table_values": "PASSED",
    "TestAggregationFunctionsEnum.test_pickle": "PASSED",
    "TestAggregationFunctionsEnum.test_sort": "PASSED",
    "TestPivot.test_group_sparse_data": "PASSED",
    "TestPivot.test_group_table": "PASSED",
    "TestPivot.test_group_table_1": "PASSED",
    "TestPivot.test_group_table_metas": "PASSED",
    "TestPivot.test_group_table_no_col_var": "PASSED",
    "TestPivot.test_group_table_no_col_var_metas": "PASSED",
    "TestPivot.test_group_table_time_var": "PASSED",
    "TestPivot.test_group_table_update": "PASSED",
    "TestPivot.test_group_table_use_cached": "PASSED",
    "TestPivot.test_pivot": "PASSED",
    "TestPivot.test_pivot_attr_combinations": "PASSED",
    "TestPivot.test_pivot_data_subset": "PASSED",
    "TestPivot.test_pivot_disc_val_var": "PASSED",
    "TestPivot.test_pivot_no_col_var": "PASSED",
    "TestPivot.test_pivot_no_val_var": "PASSED",
    "TestPivot.test_pivot_renaming_domain": "PASSED",
    "TestPivot.test_pivot_time_val_var": "PASSED",
    "TestPivot.test_pivot_total": "PASSED",
    "TestPivot.test_pivot_update": "PASSED",
    "TestOWPivot.test_select_by_click": "FAILED"
}
diff --git a/Orange/widgets/data/owpivot.py b/Orange/widgets/data/owpivot.py
index 6f3074a77..8906c252f 100644
--- a/Orange/widgets/data/owpivot.py
+++ b/Orange/widgets/data/owpivot.py
@@ -921,8 +921,8 @@ class OWPivot(OWWidget):
             self.row_feature = model[0]
         model = self.controls.val_feature.model()
         if model and len(model) > 2:
-            self.val_feature = domain.variables[0] \
-                if domain.variables[0] in model else model[2]
+            allvars = domain.variables + domain.metas
+            self.val_feature = allvars[0] if allvars[0] in model else model[2]
 
     def commit(self):
         def send_outputs(pivot_table, filtered_data, grouped_data):

{"Orange/widgets/data/owpivot.py": "# pylint: disable=missing-docstring\nfrom typing import Iterable, Set\nfrom collections import defaultdict\nfrom itertools import product, chain\n\nimport numpy as np\nfrom scipy import sparse as sp\n\nfrom AnyQt.QtCore import (Qt, QSize, QItemSelection, QItemSelectionModel,\n                          pyqtSignal)\nfrom AnyQt.QtGui import QStandardItem, QColor, QStandardItemModel\nfrom AnyQt.QtWidgets import (QTableView, QSizePolicy, QHeaderView,\n                             QStyledItemDelegate, QCheckBox, QFrame, QWidget,\n                             QGridLayout)\n\nfrom Orange.data import (Table, DiscreteVariable, Variable, Domain,\n                         ContinuousVariable, TimeVariable)\nfrom Orange.data.domain import filter_visible\nfrom Orange.data.util import get_unique_names_duplicates, get_unique_names\nfrom Orange.data.filter import FilterContinuous, FilterDiscrete, Values\nfrom Orange.statistics.util import (nanmin, nanmax, nanunique, nansum, nanvar,\n                                    nanmean, nanmedian, nanmode, bincount)\nfrom Orange.util import Enum\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import (Setting, ContextSetting,\n                                     DomainContextHandler)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Input, Output, Msg\n\n\nBorderRole = next(gui.OrangeUserRole)\nBorderColorRole = next(gui.OrangeUserRole)\n\n\nclass AggregationFunctionsEnum(Enum):\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = range(10)\n\n    def __init__(self, *_, **__):\n        super().__init__()\n        self.func = None\n\n    @property\n    def value(self):\n        return self._value_\n\n    def __call__(self, *args):\n        return self.func(args)  # pylint: disable=not-callable\n\n    def __str__(self):\n        return self._name_.replace(\"_\", \" \")\n\n    def __gt__(self, other):\n        return self._value_ > other.value\n\n\nclass Pivot:\n    Functions = AggregationFunctionsEnum\n    (Count, Count_defined, Sum, Mean, Min, Max,\n     Mode, Median, Var, Majority) = Functions\n\n    AutonomousFunctions = (Count,)\n    AnyVarFunctions = (Count_defined,)\n    ContVarFunctions = (Sum, Mean, Min, Max, Mode, Median, Var)\n    DiscVarFunctions = (Majority,)\n    TimeVarFunctions = (Mean, Min, Max, Mode, Median)\n    FloatFunctions = (Count, Count_defined, Sum, Var)\n\n    class Tables:\n        table = None  # type: Table\n        total_h = None  # type: Table\n        total_v = None  # type: Table\n        total = None  # type: Table\n\n        def __call__(self):\n            return self.table, self.total_h, self.total_v, self.total\n\n    def __init__(self, table: Table, agg_funs: Iterable[Functions],\n                 row_var: Variable, col_var: Variable = None,\n                 val_var: Variable = None):\n        self._group_tables = self.Tables()\n        self._pivot_tables = self.Tables()\n        self._table = table\n        self._row_var = row_var\n        self._col_var = col_var if col_var else row_var\n        self.renamed = []\n\n        if not table:\n            return\n        if not self._row_var.is_primitive():\n            raise TypeError(\"Row variable should be DiscreteVariable\"\n                            \" or ContinuousVariable\")\n        if self._col_var and not self._col_var.is_discrete:\n            raise TypeError(\"Column variable should be DiscreteVariable\")\n\n        self._row_var_col = table.get_column_view(row_var)[0].astype(np.float)\n        self._col_var_col = table.get_column_view(self._col_var)[0].astype(np.float)\n        self._row_var_groups = nanunique(self._row_var_col)\n        self._col_var_groups = nanunique(self._col_var_col)\n\n        self._total_var = DiscreteVariable(\"Total\", values=(\"total\", ))\n        self._current_agg_functions = sorted(agg_funs)\n        self._indepen_agg_done = {}  # type: Dict[Functions, int]\n        self._depen_agg_done = {}  # type: Dict[Functions, Dict[Variable, int]]\n\n        self._initialize(agg_funs, val_var)\n\n    @property\n    def group_table(self) -> Table:\n        table = self._group_tables.table\n        if not table or len(table) == 0:\n            return None\n        indices = [0, 1] if not self.single_var_grouping else [0]\n        for f in self._current_agg_functions:\n            if f in self._indepen_agg_done:\n                indices.append(self._indepen_agg_done[f])\n        for v in self._table.domain.variables + self._table.domain.metas:\n            for f in self._current_agg_functions:\n                if f in self._depen_agg_done and v in self._depen_agg_done[f]:\n                    indices.append(self._depen_agg_done[f][v])\n        return table[:, indices]\n\n    @property\n    def pivot_table(self) -> Table:\n        return self._pivot_tables.table\n\n    @property\n    def pivot_total_h(self) -> Table:\n        return self._pivot_tables.total_h\n\n    @property\n    def pivot_total_v(self) -> Table:\n        return self._pivot_tables.total_v\n\n    @property\n    def pivot_total(self) -> Table:\n        return self._pivot_tables.total\n\n    @property\n    def pivot_tables(self) -> Table:\n        return self._pivot_tables()\n\n    @property\n    def single_var_grouping(self) -> bool:\n        return self._row_var is self._col_var\n\n    def update_group_table(self, agg_funs: Iterable[Functions],\n                           val_var: Variable = None):\n        if not self._group_tables:\n            return\n        self._current_agg_functions = sorted(agg_funs)\n        agg_funs = set(self._indepen_agg_done.keys()) | \\\n            set(self._depen_agg_done.keys()) | set(agg_funs)\n        self._initialize(sorted(agg_funs), val_var)\n\n    def _initialize(self, agg_funs, val_var):\n        var_indep_funs, var_dep_funs = self.__group_aggregations(agg_funs)\n        self._create_group_tables(var_indep_funs, var_dep_funs)\n        self.__reference_aggregations(var_indep_funs, var_dep_funs)\n        self._create_pivot_tables(val_var)\n\n    def __group_aggregations(self, agg_funs):\n        auto_funcs = self.AutonomousFunctions\n        var_indep_funs = [fun for fun in agg_funs if fun in auto_funcs]\n        var_dep_funs = []\n        attrs = self._table.domain.variables + self._table.domain.metas\n        prod = product(filter_visible(attrs),\n                       [fun for fun in agg_funs if fun not in auto_funcs])\n        for var, fun in prod:\n            if self.__include_aggregation(fun, var):\n                var_dep_funs.append((var, fun))\n        return var_indep_funs, var_dep_funs\n\n    def __include_aggregation(self, fun, var):\n        return fun in self.ContVarFunctions and var.is_continuous or \\\n               fun in self.DiscVarFunctions and var.is_discrete or \\\n               fun in self.AnyVarFunctions\n\n    def __reference_aggregations(self, var_indep_funs, var_dep_funs):\n        self._indepen_agg_done = {}\n        self._depen_agg_done = defaultdict(dict)\n        i = 1 - int(bool(self.single_var_grouping))\n        for i, fun in enumerate(var_indep_funs, i + 1):\n            self._indepen_agg_done[fun] = i\n        for j, (var, fun) in enumerate(var_dep_funs, i + 1):\n            self._depen_agg_done[fun].update({var: j})\n\n    def _create_group_tables(self, var_indep_funs, var_dep_funs):\n        attrs = [ContinuousVariable(f\"({str(fun).lower()})\")\n                 for fun in var_indep_funs]\n        for var, fun in var_dep_funs:\n            name = f\"{var.name} ({str(fun).lower()})\"\n            if fun in self.DiscVarFunctions:\n                attrs.append(DiscreteVariable(name, var.values))\n            else:\n                if isinstance(var, TimeVariable) and \\\n                        fun in self.TimeVarFunctions:\n                    attrs.append(TimeVariable(name, have_date=var.have_date,\n                                              have_time=var.have_time))\n                else:\n                    attrs.append(ContinuousVariable(name))\n        args = (var_indep_funs, var_dep_funs, attrs)\n        for t, var in ((\"table\", None), (\"total_h\", self._col_var),\n                       (\"total_v\", self._row_var), (\"total\", self._total_var)):\n            setattr(self._group_tables, t, self.__get_group_table(var, *args))\n\n    def __get_group_table(self, var, var_indep_funs, var_dep_funs, attrs):\n        if var is self._total_var:\n            group_tab = self._group_tables.total\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._total_var]\n            combs = np.array([[0]])\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (~np.isnan(self._col_var_col)))[0]]\n        elif var is self._row_var or self.single_var_grouping:\n            group_tab = self._group_tables.total_v\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._row_var]\n            combs = self._row_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._col_var_col)) &\n                                     (self._row_var_col == x[0]))[0]]\n        elif var is self._col_var:\n            group_tab = self._group_tables.total_h\n            offset = int(bool(not self.single_var_grouping))\n            leading_vars = [self._col_var]\n            combs = self._col_var_groups[:, None]\n            sub_table_getter = lambda x: \\\n                self._table[np.where((~np.isnan(self._row_var_col)) &\n                                     (self._col_var_col == x[0]))[0]]\n        else:\n            group_tab = self._group_tables.table\n            offset = 0\n            leading_vars = [self._row_var, self._col_var]\n            combs = np.array(list(product(self._row_var_groups,\n                                          self._col_var_groups)))\n            sub_table_getter = lambda x: \\\n                self._table[np.where((self._row_var_col == x[0])\n                                     & (self._col_var_col == x[1]))[0]]\n\n        if not combs.shape[0]:\n            return None\n\n        n = len(var_indep_funs) + len(var_dep_funs)\n        X = np.zeros((len(combs), n), dtype=float)\n        for i, comb in enumerate(combs):\n            sub_table = sub_table_getter(comb)\n            j = -1\n            for j, fun in enumerate(var_indep_funs):\n                if fun in self._indepen_agg_done:\n                    # TODO - optimize - after this line is executed,\n                    # the whole column is already set\n                    X[:, j] = group_tab.X[:, self._indepen_agg_done[fun] - offset]\n                else:\n                    X[i, j] = fun(sub_table)\n            for k, (v, fun) in enumerate(var_dep_funs, j + 1):\n                if fun in self._depen_agg_done:\n                    X[:, k] = group_tab.X[:, self._depen_agg_done[fun][v] - offset]\n                else:\n                    X[i, k] = fun(sub_table.get_column_view(v)[0])\n\n        #rename leading vars (seems the easiest) if needed\n        current = [var.name for var in attrs]\n        uniq_leading_vars = []\n        for v in leading_vars:\n            uniq = get_unique_names(current, v.name)\n            if uniq != v.name:\n                self.renamed.append(v.name)\n                v = v.copy(name=uniq)\n            uniq_leading_vars.append(v)\n            current.append(uniq)\n\n        return Table(Domain(uniq_leading_vars + attrs), np.hstack((combs, X)))\n\n    def update_pivot_table(self, val_var: Variable):\n        self._create_pivot_tables(val_var)\n\n    def _create_pivot_tables(self, val_var):\n        if not self._group_tables.table:\n            self._pivot_tables = self.Tables()\n            return\n\n        agg_funs = [fun for fun in self._current_agg_functions\n                    if fun in self.AutonomousFunctions\n                    or val_var and self.__include_aggregation(fun, val_var)]\n        X, X_h, X_v, X_t = self.__get_pivot_tab_x(val_var, agg_funs)\n        dom, dom_h, dom_v, dom_t = self.__get_pivot_tab_domain(\n            val_var, X, X_h, X_v, X_t, agg_funs)\n        for t, d, x in ((\"table\", dom, X), (\"total_h\", dom_h, X_h),\n                        (\"total_v\", dom_v, X_v), (\"total\", dom_t, X_t)):\n            setattr(self._pivot_tables, t, Table(d, x))\n\n    # pylint: disable=invalid-name\n    def __get_pivot_tab_domain(self, val_var, X, X_h, X_v, X_t, agg_funs):\n        def map_values(index, _X):\n            values = np.unique(_X[:, index])\n            values = np.delete(values, np.where(values == \"nan\")[0])\n            for j, value in enumerate(values):\n                _X[:, index][_X[:, index] == value] = j\n            return values\n\n        create_time_var = \\\n            isinstance(val_var, TimeVariable) and \\\n            all(fun in self.TimeVarFunctions for fun in agg_funs)\n        create_cont_var = \\\n            not val_var or val_var.is_continuous and \\\n            (not isinstance(val_var, TimeVariable) or\n             all(fun in self.FloatFunctions for fun in agg_funs))\n\n        vals = np.array(self._col_var.values)[self._col_var_groups.astype(int)]\n        if create_time_var:\n            kwargs = {\"have_date\": val_var.have_date,\n                      \"have_time\": val_var.have_time}\n            attrs = [[TimeVariable(f\"{v}\", **kwargs) for v in vals]] * 2\n            attrs.extend([[TimeVariable(\"Total\", **kwargs)]] * 2)\n        elif create_cont_var:\n            attrs = [[ContinuousVariable(f\"{v}\", 1) for v in vals]] * 2\n            attrs.extend([[ContinuousVariable(\"Total\", 1)]] * 2)\n        else:\n            attrs = []\n            for x in (X, X_h):\n                attrs.append([DiscreteVariable(f\"{v}\", map_values(i, x))\n                              for i, v in enumerate(vals, 2)])\n            for x in (X_v, X_t):\n                attrs.append([DiscreteVariable(\"Total\", map_values(0, x))])\n        row_var_h = DiscreteVariable(self._row_var.name, values=[\"Total\"])\n        aggr_attr = DiscreteVariable('Aggregate', [str(f) for f in agg_funs])\n\n        same_row_col = self._col_var is self._row_var\n\n        extra_vars = [self._row_var, aggr_attr]\n        uniq_a = get_unique_names_duplicates([v.name for v in extra_vars]\n                                             + [atr.name for atr in attrs[0]])\n        for (idx, var), u in zip(enumerate(chain(extra_vars, attrs[0])), uniq_a):\n            if var.name == u:\n                continue\n            if idx == 0:\n                self.renamed.append(self._row_var.name)\n                self._row_var = self._row_var.copy(name=u)\n                if same_row_col:\n                    self._col_var = self._row_var\n                row_var_h = row_var_h.copy(name=u)\n            elif idx == 1:\n                self.renamed.append(aggr_attr.name)\n                aggr_attr = aggr_attr.copy(name=u)\n            else:\n                self.renamed.append(var.name)\n                attrs[0][idx-2] = var.copy(name=u)\n                attrs[1][idx-2] = var.copy(name=u)\n\n        if same_row_col:\n            vals = tuple(v.name for v in attrs[0])\n            self._row_var.make(self._row_var.name, values=vals)\n            vals = tuple(v.name for v in attrs[2])\n            row_var_h.make(row_var_h.name, vals)\n\n        return (Domain([self._row_var, aggr_attr] + attrs[0]),\n                Domain([row_var_h, aggr_attr] + attrs[1]),\n                Domain(attrs[2]), Domain(attrs[3]))\n\n    def __get_pivot_tab_x(self, val_var, agg_funs):\n        gt = self._group_tables\n        n_fun = len(agg_funs)\n        n_rows, n_cols = len(self._row_var_groups), len(self._col_var_groups)\n        is_float_type = not val_var or val_var.is_continuous\n        if isinstance(val_var, TimeVariable):\n            is_float_type = \\\n                all(fun in self.TimeVarFunctions for fun in agg_funs) or \\\n                all(fun in self.FloatFunctions for fun in agg_funs)\n        kwargs = {\"fill_value\": np.nan, \"dtype\": float} if is_float_type \\\n            else {\"fill_value\": \"\", \"dtype\": object}\n        X = np.full((n_rows * n_fun, 2 + n_cols), **kwargs)\n        X_h = np.full((n_fun, 2 + n_cols), **kwargs)\n        X_v = np.full((n_rows * n_fun, 1), **kwargs)\n        X_t = np.full((n_fun, 1), **kwargs)\n        for i, fun in enumerate(agg_funs):\n            args = (val_var, fun, is_float_type)\n            X[i::n_fun, 2:] = self.__rows_for_function(n_rows, n_cols, *args)\n            X[i::n_fun, :2] = np.array([[row_val, agg_funs.index(fun)] for\n                                        row_val in self._row_var_groups])\n            X_h[i, :2] = 0, agg_funs.index(fun)\n            X_h[i, 2:] = self.__total_for_function(gt.total_h, *args)\n            X_v[i::n_fun, 0] = self.__total_for_function(gt.total_v, *args)\n            X_t[i] = self.__total_for_function(gt.total, *args)\n        return X, X_h, X_v, X_t\n\n    def __total_for_function(self, group_tab, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        ref -= int(bool(not self.single_var_grouping))\n        return self.__check_continuous(val_var, group_tab.X[:, ref],\n                                       fun, is_float_type)\n\n    def __rows_for_function(self, n_rows, n_cols, val_var, fun, is_float_type):\n        ref = self._indepen_agg_done.get(fun, None) \\\n              or self._depen_agg_done[fun][val_var]\n        column = self._group_tables.table.X[:, ref]\n        if self.single_var_grouping:\n            rows = np.full((n_rows, n_cols), fun(np.array([]), ), dtype=float)\n            rows[np.diag_indices_from(rows)] = column\n        else:\n            rows = column.reshape(n_rows, n_cols)\n        return self.__check_continuous(val_var, rows, fun, is_float_type)\n\n    def __check_continuous(self, val_var, column, fun, is_float_type):\n        if val_var and not val_var.is_continuous:\n            column = column.astype(str)\n            if fun in self.DiscVarFunctions:\n                for j, val in enumerate(val_var.values):\n                    column[column == str(float(j))] = val\n        elif isinstance(val_var, TimeVariable) and not is_float_type:\n            shape = column.shape\n            column = column.flatten()\n            column_ = column.astype(str)\n            if fun in self.TimeVarFunctions:\n                for i in range(column.shape[0]):\n                    if not np.isnan(column[i]):\n                        column_[i] = val_var.repr_val(column[i])\n            return column_.reshape(shape)\n        return column\n\n    @staticmethod\n    def count_defined(x):\n        if x.shape[0] == 0:\n            return 0\n        if x.size and np.issubdtype(x.dtype, np.number) and not sp.issparse(x):\n            nans = np.isnan(x).sum(axis=0)\n        elif sp.issparse(x) and x.size:\n            nans = np.bincount(x.nonzero()[1], minlength=x.shape[1])\n            x = x.tocsc()\n        else:\n            x_str = x.astype(str)\n            nans = ((x_str == \"nan\") | (x_str == \"\")).sum(axis=0) \\\n                if x.size else np.zeros(x.shape[1])\n        return x.shape[0] - nans\n\n    @staticmethod\n    def stat(x, f):\n        return f(x.astype(np.float), axis=0) if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def mode(x):\n        return Pivot.stat(x, nanmode).mode if x.shape[0] > 0 else np.nan\n\n    @staticmethod\n    def majority(x):\n        if x.shape[0] == 0:\n            return np.nan\n        counts = bincount(x)[0]\n        return np.argmax(counts) if counts.shape[0] else np.nan\n\n    Count.func = lambda x: len(x[0])\n    Count_defined.func = lambda x: Pivot.count_defined(x[0])\n    Sum.func = lambda x: nansum(x[0], axis=0) if x[0].shape[0] > 0 else 0\n    Mean.func = lambda x: Pivot.stat(x[0], nanmean)\n    Min.func = lambda x: Pivot.stat(x[0], nanmin)\n    Max.func = lambda x: Pivot.stat(x[0], nanmax)\n    Median.func = lambda x: Pivot.stat(x[0], nanmedian)\n    Mode.func = lambda x: Pivot.mode(x[0])\n    Var.func = lambda x: Pivot.stat(x[0], nanvar)\n    Majority.func = lambda x: Pivot.majority(x[0])\n\n\nclass BorderedItemDelegate(QStyledItemDelegate):\n    def paint(self, painter, option, index):\n        \"\"\"Overloads `paint` to draw borders\"\"\"\n        QStyledItemDelegate.paint(self, painter, option, index)\n        if index.data(BorderRole):\n            painter.save()\n            painter.setPen(index.data(BorderColorRole))\n            rect = option.rect\n            painter.drawLine(rect.topLeft(), rect.topRight())\n            painter.restore()\n\n\nclass PivotTableView(QTableView):\n    selection_changed = pyqtSignal()\n\n    TOTAL_STRING = \"Total\"\n\n    def __init__(self):\n        super().__init__(editTriggers=QTableView.NoEditTriggers)\n        self._n_classesv = None  # number of row_feature values\n        self._n_classesh = None  # number of col_feature values\n        self._n_agg_func = None  # number of aggregation functions\n        self._n_leading_rows = None  # number of leading rows\n        self._n_leading_cols = None  # number of leading columns\n\n        self.table_model = QStandardItemModel(self)\n        self.setModel(self.table_model)\n        self.horizontalHeader().hide()\n        self.verticalHeader().hide()\n        self.horizontalHeader().setMinimumSectionSize(60)\n        self.setShowGrid(False)\n        self.setSizePolicy(QSizePolicy.MinimumExpanding,\n                           QSizePolicy.MinimumExpanding)\n        self.setItemDelegate(BorderedItemDelegate())\n        self.pressed.connect(self.__cell_clicked)\n        self.clicked.connect(self.__cell_clicked)\n        self.entered.connect(self.__cell_entered)\n        self.__clicked_cell = None\n\n    @property\n    def add_agg_column(self) -> bool:\n        return self._n_agg_func > 1\n\n    def __cell_entered(self, model_index):\n        if self.__clicked_cell is None:\n            return\n        index = self.table_model.index\n        selection = None\n        i_end, j_end = model_index.row(), model_index.column()\n        i_start, j_start = self.__clicked_cell\n        i_start, i_end = sorted([i_start, i_end])\n        j_start, j_end = sorted([j_start, j_end])\n        if i_start >= self._n_leading_rows and j_start >= self._n_leading_cols:\n            i_start = (i_start - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows\n            i_end = (i_end - self._n_leading_rows) // self._n_agg_func * \\\n                self._n_agg_func + self._n_leading_rows + self._n_agg_func - 1\n            start, end = index(i_start, j_start), index(i_end, j_end)\n            selection = QItemSelection(start, end)\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n        self.selection_changed.emit()\n\n    def __cell_clicked(self, model_index):\n        i, j = model_index.row(), model_index.column()\n        self.__clicked_cell = (i, j)\n        m, n = self.table_model.rowCount(), self.table_model.columnCount()\n        index = self.table_model.index\n        selection = None\n        if i > m - self._n_agg_func - 1 and j == n - 1:\n            start_index = index(self._n_leading_rows, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(m - 1, n - 1))\n        elif i == self._n_leading_rows - 1 or i > m - self._n_agg_func - 1:\n            start_index = index(self._n_leading_rows, j)\n            selection = QItemSelection(start_index, index(m - 1, j))\n        elif j in (self._n_leading_cols - 1, n - 1, 1):\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            start_index = index(i_start, self._n_leading_cols)\n            selection = QItemSelection(start_index, index(i_end, n - 1))\n        elif i >= self._n_leading_rows and j >= self._n_leading_cols:\n            i_start = (i - self._n_leading_rows) // self._n_agg_func * \\\n                      self._n_agg_func + self._n_leading_rows\n            i_end = i_start + self._n_agg_func - 1\n            selection = QItemSelection(index(i_start, j), index(i_end, j))\n\n        if selection is not None:\n            self.selectionModel().select(\n                selection, QItemSelectionModel.ClearAndSelect)\n\n    def mouseReleaseEvent(self, e):\n        super().mouseReleaseEvent(e)\n        self.selection_changed.emit()\n\n    def update_table(self, titleh: str, titlev: str, table: Table,\n                     table_total_h: Table, table_total_v: Table,\n                     table_total: Table):\n        self.clear()\n        if not table:\n            return\n\n        self._initialize(table, table_total_h)\n        self._set_headers(titleh, titlev, table)\n        self._set_values(table[:, 2:])\n        self._set_totals(table_total_h[:, 2:], table_total_v, table_total)\n        self._draw_lines()\n        self._resize(table)\n\n    def _initialize(self, table, table_total_h):\n        self._n_classesv = int(len(table) / len(table_total_h))\n        self._n_classesh = table.X.shape[1] - 2\n        self._n_agg_func = len(table_total_h)\n        self._n_leading_rows = 2\n        self._n_leading_cols = 2 + int(len(table_total_h) > 1)\n\n    def _set_headers(self, titleh, titlev, table):\n        self.__set_horizontal_title(titleh)\n        self.__set_vertical_title(titlev)\n        self.__set_flags_title()\n        self.__set_horizontal_headers(table)\n        self.__set_vertical_headers(table)\n\n    def __set_horizontal_title(self, titleh):\n        item = QStandardItem()\n        item.setData(titleh, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignCenter)\n        self.table_model.setItem(0, self._n_leading_cols, item)\n        self.setSpan(0, self._n_leading_cols, 1, self._n_classesh + 3)\n\n    def __set_vertical_title(self, titlev):\n        item = QStandardItem()\n        item.setData(titlev, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)\n        self.setItemDelegateForColumn(0, gui.VerticalItemDelegate(extend=True))\n        self.table_model.setItem(self._n_leading_rows, 0, item)\n        row_span = self._n_classesv * self._n_agg_func + 1\n        self.setSpan(self._n_leading_rows, 0, row_span, 1)\n\n    def __set_flags_title(self):\n        item = self.table_model.item(0, self._n_leading_cols)\n        item.setFlags(Qt.NoItemFlags)\n        item = self.table_model.item(self._n_leading_rows, 0)\n        item.setFlags(Qt.NoItemFlags)\n        for i, j in product(range(self._n_leading_rows),\n                            range(self._n_leading_cols)):\n            item = QStandardItem()\n            item.setFlags(Qt.NoItemFlags)\n            self.table_model.setItem(i, j, item)\n\n    def __set_horizontal_headers(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if not self.add_agg_column:\n            labels[0] = str(table[0, 1])\n        for i, label in enumerate(labels, self._n_leading_cols - 1):\n            self.table_model.setItem(1, i, self._create_header_item(label))\n\n    def __set_vertical_headers(self, table):\n        labels = [(str(row[0]), str(row[1])) for row in table]\n        i = self._n_leading_rows - 1\n        for i, (l1, l2) in enumerate(labels, self._n_leading_rows):\n            l1 = \"\" if (i - self._n_leading_rows) % self._n_agg_func else l1\n            self.table_model.setItem(i, 1, self._create_header_item(l1))\n            if self.add_agg_column:\n                self.table_model.setItem(i, 2, self._create_header_item(l2))\n\n        if self.add_agg_column:\n            labels = [str(row[1]) for row in table[:self._n_agg_func]]\n            start = self._n_leading_rows + self._n_agg_func * self._n_classesv\n            for j, l2 in enumerate(labels, i + 1):\n                l1 = self.TOTAL_STRING if j == start else \"\"\n                self.table_model.setItem(j, 1, self._create_header_item(l1))\n                self.table_model.setItem(j, 2, self._create_header_item(l2))\n        else:\n            item = self._create_header_item(self.TOTAL_STRING)\n            self.table_model.setItem(i + 1, 1, item)\n\n    def _set_values(self, table):\n        for i, j in product(range(len(table)), range(len(table[0]))):\n            value = table[i, j]\n            item = self._create_value_item(str(value))\n            self.table_model.setItem(i + self._n_leading_rows,\n                                     j + self._n_leading_cols, item)\n\n    def _set_totals(self, table_total_h, table_total_v, table_total):\n        def set_total_item(table, get_row, get_col):\n            for i, j in product(range(len(table)), range(len(table[0]))):\n                item = self._create_header_item(str(table[i, j]))\n                self.table_model.setItem(get_row(i), get_col(j), item)\n\n        last_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        last_col = self._n_leading_cols + self._n_classesh\n        set_total_item(table_total_v, lambda x: x + self._n_leading_rows,\n                       lambda x: last_col)\n        set_total_item(table_total_h, lambda x: x + last_row,\n                       lambda x: x + self._n_leading_cols)\n        set_total_item(table_total, lambda x: x + last_row, lambda x: last_col)\n\n    def _create_header_item(self, text):\n        bold_font = self.table_model.invisibleRootItem().font()\n        bold_font.setBold(True)\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setFont(bold_font)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled)\n        return item\n\n    @staticmethod\n    def _create_value_item(text):\n        item = QStandardItem()\n        item.setData(text, Qt.DisplayRole)\n        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)\n        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)\n        return item\n\n    def _draw_lines(self):\n        end_col = self._n_leading_cols + self._n_classesh + 1\n        total_row = self._n_leading_rows + self._n_classesv * self._n_agg_func\n        indices = [(total_row, j) for j in range(1, end_col)]\n        for i in range(self._n_classesv):\n            inner_row = self._n_agg_func * i + self._n_leading_rows\n            inner_indices = [(inner_row, j) for j in range(1, end_col)]\n            indices = indices + inner_indices\n            if not self.add_agg_column:\n                break\n        for i, j in indices:\n            item = self.table_model.item(i, j)\n            item.setData(\"t\", BorderRole)\n            item.setData(QColor(160, 160, 160), BorderColorRole)\n\n    def _resize(self, table):\n        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]\n        if len(' '.join(labels)) < 120:\n            self.horizontalHeader().setSectionResizeMode(\n                QHeaderView.ResizeToContents)\n        else:\n            self.horizontalHeader().setDefaultSectionSize(60)\n\n    def get_selection(self) -> Set:\n        m, n = self._n_leading_rows, self._n_leading_cols\n        return {(ind.row() - m, ind.column() - n)\n                for ind in self.selectedIndexes()}\n\n    def set_selection(self, indexes: Set):\n        selection = QItemSelection()\n        index = self.model().index\n        for row, col in indexes:\n            sel = index(row + self._n_leading_rows, col + self._n_leading_cols)\n            selection.select(sel, sel)\n        self.selectionModel().select(\n            selection, QItemSelectionModel.ClearAndSelect)\n\n    def clear(self):\n        self.table_model.clear()\n\n\nclass OWPivot(OWWidget):\n    name = \"Pivot Table\"\n    description = \"Reshape data table based on column values.\"\n    icon = \"icons/Pivot.svg\"\n    priority = 1000\n    keywords = [\"pivot\", \"group\", \"aggregate\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table, default=True)\n\n    class Outputs:\n        pivot_table = Output(\"Pivot Table\", Table, default=True)\n        filtered_data = Output(\"Filtered Data\", Table)\n        grouped_data = Output(\"Grouped Data\", Table)\n\n    class Warning(OWWidget.Warning):\n        # TODO - inconsistent for different variable types\n        no_col_feature = Msg(\"Column feature should be selected.\")\n        cannot_aggregate = Msg(\"Some aggregations ({}) cannot be computed.\")\n        renamed_vars = Msg(\"Some variables have been renamed in some tables\"\n                           \"to avoid duplicates.\\n{}\")\n        too_many_values = Msg(\"Selected variable has too many values.\")\n        no_variables = Msg(\"At least 1 primitive variable is required.\")\n\n    settingsHandler = DomainContextHandler()\n    row_feature = ContextSetting(None)\n    col_feature = ContextSetting(None)\n    val_feature = ContextSetting(None)\n    sel_agg_functions = Setting(set([Pivot.Count]))\n    selection = Setting(set(), schema_only=True)\n    auto_commit = Setting(True)\n\n    AGGREGATIONS = (Pivot.Count,\n                    Pivot.Count_defined,\n                    None,  # separator\n                    Pivot.Sum,\n                    Pivot.Mean,\n                    Pivot.Var,\n                    Pivot.Median,\n                    2,  # column break\n                    Pivot.Mode,\n                    Pivot.Min,\n                    Pivot.Max,\n                    None,\n                    Pivot.Majority)\n\n    MAX_VALUES = 100\n\n    def __init__(self):\n        super().__init__()\n        self.data = None  # type: Table\n        self.pivot = None  # type: Pivot\n        self.__pending_selection = self.selection  # type: Set\n        self._add_control_area_controls()\n        self._add_main_area_controls()\n\n    def _add_control_area_controls(self):\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Rows\"),\n                     self, \"row_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Columns\"),\n                     self, \"col_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(Same as rows)\",\n                                       valid_types=DiscreteVariable),\n                     callback=self.__feature_changed,\n                     orientation=Qt.Horizontal)\n        gui.comboBox(gui.vBox(self.controlArea, box=\"Values\"),\n                     self, \"val_feature\",\n                     contentsLength=14,\n                     searchable=True,\n                     model=DomainModel(placeholder=\"(None)\"),\n                     callback=self.__val_feature_changed,\n                     orientation=Qt.Horizontal)\n        self.__add_aggregation_controls()\n        gui.rubber(self.controlArea)\n        gui.auto_apply(self.buttonsArea, self, \"auto_commit\")\n\n    def __add_aggregation_controls(self):\n        def new_inbox():\n            nonlocal row, col, inbox\n            inbox = QWidget()\n            layout = QGridLayout()\n            inbox.setLayout(layout)\n            layout.setContentsMargins(0, 0, 0, 0)\n            box.layout().addWidget(inbox)\n            row = col = 0\n\n        box = gui.vBox(self.controlArea, \"Aggregations\")\n        row = col = 0\n        inbox = None\n        new_inbox()\n        self.aggregation_checkboxes = []  # for test purposes\n        for agg in self.AGGREGATIONS:\n            if agg is None:\n                line = QFrame()\n                line.setFrameShape(QFrame.HLine)\n                line.setLineWidth(1)\n                line.setFrameShadow(QFrame.Sunken)\n                box.layout().addWidget(line)\n                new_inbox()\n                continue\n            elif agg == 2:\n                col += 1\n                row = 0\n                continue\n            check_box = QCheckBox(str(agg), inbox)\n            check_box.setChecked(agg in self.sel_agg_functions)\n            check_box.clicked.connect(lambda *args, a=agg:\n                                      self.__aggregation_cb_clicked(a, args[0]))\n            inbox.layout().addWidget(check_box, row, col)\n            self.aggregation_checkboxes.append(check_box)\n            row += 1\n\n    def _add_main_area_controls(self):\n        self.table_view = PivotTableView()\n        self.table_view.selection_changed.connect(self.__invalidate_filtered)\n        self.mainArea.layout().addWidget(self.table_view)\n\n    @property\n    def no_col_feature(self):\n        return self.col_feature is None and self.row_feature is not None \\\n            and self.row_feature.is_continuous\n\n    @property\n    def skipped_aggs(self):\n        def add(fun):\n            data, var = self.data, self.val_feature\n            return data and not var and fun not in Pivot.AutonomousFunctions \\\n                or var and var.is_discrete and fun in Pivot.ContVarFunctions \\\n                or var and var.is_continuous and fun in Pivot.DiscVarFunctions\n        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]\n        return \", \".join(sorted(skipped))\n\n    @property\n    def data_has_primitives(self):\n        if not self.data:\n            return False\n        domain = self.data.domain\n        return any(v.is_primitive() for v in domain.variables + domain.metas)\n\n    def __feature_changed(self):\n        self.selection = set()\n        self.pivot = None\n        self.commit()\n\n    def __val_feature_changed(self):\n        self.selection = set()\n        if self.no_col_feature or not self.pivot:\n            return\n        self.pivot.update_pivot_table(self.val_feature)\n        self.commit()\n\n    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):\n        self.selection = set()\n        if checked:\n            self.sel_agg_functions.add(agg_fun)\n        else:\n            self.sel_agg_functions.remove(agg_fun)\n        if self.no_col_feature or not self.pivot or not self.data:\n            return\n        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)\n        self.commit()\n\n    def __invalidate_filtered(self):\n        self.selection = self.table_view.get_selection()\n        self.commit()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.closeContext()\n        self.selection = set()\n        self.data = data\n        self.pivot = None\n        self.check_data()\n        self.init_attr_values()\n        if self.data_has_primitives:\n            self.openContext(self.data)\n        self.unconditional_commit()\n\n    def check_data(self):\n        self.clear_messages()\n\n    def init_attr_values(self):\n        domain = self.data.domain if self.data and len(self.data) else None\n        for attr in (\"row_feature\", \"col_feature\", \"val_feature\"):\n            getattr(self.controls, attr).model().set_domain(domain)\n            setattr(self, attr, None)\n        model = self.controls.row_feature.model()\n        if model:\n            self.row_feature = model[0]\n        model = self.controls.val_feature.model()\n        if model and len(model) > 2:\n            self.val_feature = domain.variables[0] \\\n                if domain.variables[0] in model else model[2]\n\n    def commit(self):\n        def send_outputs(pivot_table, filtered_data, grouped_data):\n            if self.data:\n                if grouped_data:\n                    grouped_data.name = self.data.name\n                if pivot_table:\n                    pivot_table.name = self.data.name\n                if filtered_data:\n                    filtered_data.name = self.data.name\n            self.Outputs.grouped_data.send(grouped_data)\n            self.Outputs.pivot_table.send(pivot_table)\n            self.Outputs.filtered_data.send(filtered_data)\n\n        self.Warning.renamed_vars.clear()\n        self.Warning.too_many_values.clear()\n        self.Warning.cannot_aggregate.clear()\n        self.Warning.no_col_feature.clear()\n\n        self.table_view.clear()\n\n        if self.pivot is None:\n            if self.data:\n                if not self.data_has_primitives:\n                    self.Warning.no_variables()\n                    send_outputs(None, None, None)\n                    return\n\n            if self.no_col_feature:\n                self.Warning.no_col_feature()\n                send_outputs(None, None, None)\n                return\n\n            if self.data:\n                col_var = self.col_feature or self.row_feature\n                col = self.data.get_column_view(col_var)[0].astype(np.float)\n                if len(nanunique(col)) >= self.MAX_VALUES:\n                    self.table_view.clear()\n                    self.Warning.too_many_values()\n                    send_outputs(None, None, None)\n                    return\n\n            self.pivot = Pivot(self.data, self.sel_agg_functions,\n                               self.row_feature,\n                               self.col_feature, self.val_feature)\n\n        if self.skipped_aggs:\n            self.Warning.cannot_aggregate(self.skipped_aggs)\n        self._update_graph()\n\n        send_outputs(self.pivot.pivot_table,\n                     self.get_filtered_data(),\n                     self.pivot.group_table)\n\n        if self.pivot.renamed:\n            self.Warning.renamed_vars(self.pivot.renamed)\n\n    def _update_graph(self):\n        if self.pivot.pivot_table:\n            col_feature = self.col_feature or self.row_feature\n            self.table_view.update_table(col_feature.name,\n                                         self.row_feature.name,\n                                         *self.pivot.pivot_tables)\n            selection = self.__pending_selection or self.selection\n            self.table_view.set_selection(selection)\n            self.selection = self.table_view.get_selection()\n            self.__pending_selection = set()\n\n    def get_filtered_data(self):\n        if not self.data or not self.selection or not self.pivot.pivot_table:\n            return None\n\n        cond = []\n        for i, j in self.selection:\n            f = []\n            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),\n                            (self.col_feature, j)]:\n                if isinstance(at, DiscreteVariable):\n                    f.append(FilterDiscrete(at, [val]))\n                elif isinstance(at, ContinuousVariable):\n                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))\n            cond.append(Values(f))\n        return Values(cond, conjunction=False)(self.data)\n\n    @staticmethod\n    def sizeHint():\n        return QSize(640, 525)\n\n    def send_report(self):\n        self.report_items((\n            (\"Row feature\", self.row_feature),\n            (\"Column feature\", self.col_feature),\n            (\"Value feature\", self.val_feature)))\n        if self.data and self.val_feature is not None:\n            self.report_table(\"\", self.table_view)\n        if not self.data:\n            self.report_items(((\"Group by\", self.row_feature),))\n            self.report_table(self.table_view)\n\n\nif __name__ == \"__main__\":\n    WidgetPreview(OWPivot).run(set_data=Table(\"heart_disease\"))\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Pivot Widget crashes with IndexError when handling tables with only meta attributes

**Description:**
When a data table containing only meta attributes is provided to the Pivot widget, it results in an unexpected crash due to an `IndexError`. This behavior prevents users from utilizing meta-only datasets effectively.

**Example Code:**
```python
def test_pivot_with_only_metas():
    zoo_data = load_zoo_dataset()
    metas_only = zoo_data[:, 17:]  # Select only meta attributes
    pivot_widget.set_data(metas_only)
    # Expected: Pivot widget handles metas-only table without errors
```

**Expected Behavior:**
The Pivot widget should gracefully handle tables that include only meta attributes. This could involve disabling certain features that rely on regular attributes or displaying an informative warning to the user, ensuring the widget remains stable.

**Actual Behavior:**
An `IndexError` occurs with the message `'tuple index out of range'`, causing the Pivot widget to crash when attempting to access variables that are not present in the meta-only dataset.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/data/owpivot.py
# pylint: disable=missing-docstring
from typing import Iterable, Set
from collections import defaultdict
from itertools import product, chain

import numpy as np
from scipy import sparse as sp

from AnyQt.QtCore import (Qt, QSize, QItemSelection, QItemSelectionModel,
                          pyqtSignal)
from AnyQt.QtGui import QStandardItem, QColor, QStandardItemModel
from AnyQt.QtWidgets import (QTableView, QSizePolicy, QHeaderView,
                             QStyledItemDelegate, QCheckBox, QFrame, QWidget,
                             QGridLayout)

from Orange.data import (Table, DiscreteVariable, Variable, Domain,
                         ContinuousVariable, TimeVariable)
from Orange.data.domain import filter_visible
from Orange.data.util import get_unique_names_duplicates, get_unique_names
from Orange.data.filter import FilterContinuous, FilterDiscrete, Values
from Orange.statistics.util import (nanmin, nanmax, nanunique, nansum, nanvar,
                                    nanmean, nanmedian, nanmode, bincount)
from Orange.util import Enum
from Orange.widgets import gui
from Orange.widgets.settings import (Setting, ContextSetting,
                                     DomainContextHandler)
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Input, Output, Msg


BorderRole = next(gui.OrangeUserRole)
BorderColorRole = next(gui.OrangeUserRole)


class AggregationFunctionsEnum(Enum):
    (Count, Count_defined, Sum, Mean, Min, Max,
     Mode, Median, Var, Majority) = range(10)

    def __init__(self, *_, **__):
        super().__init__()
        self.func = None

    @property
    def value(self):
        return self._value_

    def __call__(self, *args):
        return self.func(args)  # pylint: disable=not-callable

    def __str__(self):
        return self._name_.replace("_", " ")

    def __gt__(self, other):
        return self._value_ > other.value


class Pivot:
    Functions = AggregationFunctionsEnum
    (Count, Count_defined, Sum, Mean, Min, Max,
     Mode, Median, Var, Majority) = Functions

    AutonomousFunctions = (Count,)
    AnyVarFunctions = (Count_defined,)
    ContVarFunctions = (Sum, Mean, Min, Max, Mode, Median, Var)
    DiscVarFunctions = (Majority,)
    TimeVarFunctions = (Mean, Min, Max, Mode, Median)
    FloatFunctions = (Count, Count_defined, Sum, Var)

    class Tables:
        table = None  # type: Table
        total_h = None  # type: Table
        total_v = None  # type: Table
        total = None  # type: Table

        def __call__(self):
            return self.table, self.total_h, self.total_v, self.total

    def __init__(self, table: Table, agg_funs: Iterable[Functions],
                 row_var: Variable, col_var: Variable = None,
                 val_var: Variable = None):
        self._group_tables = self.Tables()
        self._pivot_tables = self.Tables()
        self._table = table
        self._row_var = row_var
        self._col_var = col_var if col_var else row_var
        self.renamed = []

        if not table:
            return
        if not self._row_var.is_primitive():
            raise TypeError("Row variable should be DiscreteVariable"
                            " or ContinuousVariable")
        if self._col_var and not self._col_var.is_discrete:
            raise TypeError("Column variable should be DiscreteVariable")

        self._row_var_col = table.get_column_view(row_var)[0].astype(np.float)
        self._col_var_col = table.get_column_view(self._col_var)[0].astype(np.float)
        self._row_var_groups = nanunique(self._row_var_col)
        self._col_var_groups = nanunique(self._col_var_col)

        self._total_var = DiscreteVariable("Total", values=("total", ))
        self._current_agg_functions = sorted(agg_funs)
        self._indepen_agg_done = {}  # type: Dict[Functions, int]
        self._depen_agg_done = {}  # type: Dict[Functions, Dict[Variable, int]]

        self._initialize(agg_funs, val_var)

    @property
    def group_table(self) -> Table:
        table = self._group_tables.table
        if not table or len(table) == 0:
            return None
        indices = [0, 1] if not self.single_var_grouping else [0]
        for f in self._current_agg_functions:
            if f in self._indepen_agg_done:
                indices.append(self._indepen_agg_done[f])
        for v in self._table.domain.variables + self._table.domain.metas:
            for f in self._current_agg_functions:
                if f in self._depen_agg_done and v in self._depen_agg_done[f]:
                    indices.append(self._depen_agg_done[f][v])
        return table[:, indices]

    @property
    def pivot_table(self) -> Table:
        return self._pivot_tables.table

    @property
    def pivot_total_h(self) -> Table:
        return self._pivot_tables.total_h

    @property
    def pivot_total_v(self) -> Table:
        return self._pivot_tables.total_v

    @property
    def pivot_total(self) -> Table:
        return self._pivot_tables.total

    @property
    def pivot_tables(self) -> Table:
        return self._pivot_tables()

    @property
    def single_var_grouping(self) -> bool:
        return self._row_var is self._col_var

    def update_group_table(self, agg_funs: Iterable[Functions],
                           val_var: Variable = None):
        if not self._group_tables:
            return
        self._current_agg_functions = sorted(agg_funs)
        agg_funs = set(self._indepen_agg_done.keys()) | \
            set(self._depen_agg_done.keys()) | set(agg_funs)
        self._initialize(sorted(agg_funs), val_var)

    def _initialize(self, agg_funs, val_var):
        var_indep_funs, var_dep_funs = self.__group_aggregations(agg_funs)
        self._create_group_tables(var_indep_funs, var_dep_funs)
        self.__reference_aggregations(var_indep_funs, var_dep_funs)
        self._create_pivot_tables(val_var)

    def __group_aggregations(self, agg_funs):
        auto_funcs = self.AutonomousFunctions
        var_indep_funs = [fun for fun in agg_funs if fun in auto_funcs]
        var_dep_funs = []
        attrs = self._table.domain.variables + self._table.domain.metas
        prod = product(filter_visible(attrs),
                       [fun for fun in agg_funs if fun not in auto_funcs])
        for var, fun in prod:
            if self.__include_aggregation(fun, var):
                var_dep_funs.append((var, fun))
        return var_indep_funs, var_dep_funs

    def __include_aggregation(self, fun, var):
        return fun in self.ContVarFunctions and var.is_continuous or \
               fun in self.DiscVarFunctions and var.is_discrete or \
               fun in self.AnyVarFunctions

    def __reference_aggregations(self, var_indep_funs, var_dep_funs):
        self._indepen_agg_done = {}
        self._depen_agg_done = defaultdict(dict)
        i = 1 - int(bool(self.single_var_grouping))
        for i, fun in enumerate(var_indep_funs, i + 1):
            self._indepen_agg_done[fun] = i
        for j, (var, fun) in enumerate(var_dep_funs, i + 1):
            self._depen_agg_done[fun].update({var: j})

    def _create_group_tables(self, var_indep_funs, var_dep_funs):
        attrs = [ContinuousVariable(f"({str(fun).lower()})")
                 for fun in var_indep_funs]
        for var, fun in var_dep_funs:
            name = f"{var.name} ({str(fun).lower()})"
            if fun in self.DiscVarFunctions:
                attrs.append(DiscreteVariable(name, var.values))
            else:
                if isinstance(var, TimeVariable) and \
                        fun in self.TimeVarFunctions:
                    attrs.append(TimeVariable(name, have_date=var.have_date,
                                              have_time=var.have_time))
                else:
                    attrs.append(ContinuousVariable(name))
        args = (var_indep_funs, var_dep_funs, attrs)
        for t, var in (("table", None), ("total_h", self._col_var),
                       ("total_v", self._row_var), ("total", self._total_var)):
            setattr(self._group_tables, t, self.__get_group_table(var, *args))

    def __get_group_table(self, var, var_indep_funs, var_dep_funs, attrs):
        if var is self._total_var:
            group_tab = self._group_tables.total
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._total_var]
            combs = np.array([[0]])
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._row_var_col)) &
                                     (~np.isnan(self._col_var_col)))[0]]
        elif var is self._row_var or self.single_var_grouping:
            group_tab = self._group_tables.total_v
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._row_var]
            combs = self._row_var_groups[:, None]
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._col_var_col)) &
                                     (self._row_var_col == x[0]))[0]]
        elif var is self._col_var:
            group_tab = self._group_tables.total_h
            offset = int(bool(not self.single_var_grouping))
            leading_vars = [self._col_var]
            combs = self._col_var_groups[:, None]
            sub_table_getter = lambda x: \
                self._table[np.where((~np.isnan(self._row_var_col)) &
                                     (self._col_var_col == x[0]))[0]]
        else:
            group_tab = self._group_tables.table
            offset = 0
            leading_vars = [self._row_var, self._col_var]
            combs = np.array(list(product(self._row_var_groups,
                                          self._col_var_groups)))
            sub_table_getter = lambda x: \
                self._table[np.where((self._row_var_col == x[0])
                                     & (self._col_var_col == x[1]))[0]]

        if not combs.shape[0]:
            return None

        n = len(var_indep_funs) + len(var_dep_funs)
        X = np.zeros((len(combs), n), dtype=float)
        for i, comb in enumerate(combs):
            sub_table = sub_table_getter(comb)
            j = -1
            for j, fun in enumerate(var_indep_funs):
                if fun in self._indepen_agg_done:
                    # TODO - optimize - after this line is executed,
                    # the whole column is already set
                    X[:, j] = group_tab.X[:, self._indepen_agg_done[fun] - offset]
                else:
                    X[i, j] = fun(sub_table)
            for k, (v, fun) in enumerate(var_dep_funs, j + 1):
                if fun in self._depen_agg_done:
                    X[:, k] = group_tab.X[:, self._depen_agg_done[fun][v] - offset]
                else:
                    X[i, k] = fun(sub_table.get_column_view(v)[0])

        #rename leading vars (seems the easiest) if needed
        current = [var.name for var in attrs]
        uniq_leading_vars = []
        for v in leading_vars:
            uniq = get_unique_names(current, v.name)
            if uniq != v.name:
                self.renamed.append(v.name)
                v = v.copy(name=uniq)
            uniq_leading_vars.append(v)
            current.append(uniq)

        return Table(Domain(uniq_leading_vars + attrs), np.hstack((combs, X)))

    def update_pivot_table(self, val_var: Variable):
        self._create_pivot_tables(val_var)

    def _create_pivot_tables(self, val_var):
        if not self._group_tables.table:
            self._pivot_tables = self.Tables()
            return

        agg_funs = [fun for fun in self._current_agg_functions
                    if fun in self.AutonomousFunctions
                    or val_var and self.__include_aggregation(fun, val_var)]
        X, X_h, X_v, X_t = self.__get_pivot_tab_x(val_var, agg_funs)
        dom, dom_h, dom_v, dom_t = self.__get_pivot_tab_domain(
            val_var, X, X_h, X_v, X_t, agg_funs)
        for t, d, x in (("table", dom, X), ("total_h", dom_h, X_h),
                        ("total_v", dom_v, X_v), ("total", dom_t, X_t)):
            setattr(self._pivot_tables, t, Table(d, x))

    # pylint: disable=invalid-name
    def __get_pivot_tab_domain(self, val_var, X, X_h, X_v, X_t, agg_funs):
        def map_values(index, _X):
            values = np.unique(_X[:, index])
            values = np.delete(values, np.where(values == "nan")[0])
            for j, value in enumerate(values):
                _X[:, index][_X[:, index] == value] = j
            return values

        create_time_var = \
            isinstance(val_var, TimeVariable) and \
            all(fun in self.TimeVarFunctions for fun in agg_funs)
        create_cont_var = \
            not val_var or val_var.is_continuous and \
            (not isinstance(val_var, TimeVariable) or
             all(fun in self.FloatFunctions for fun in agg_funs))

        vals = np.array(self._col_var.values)[self._col_var_groups.astype(int)]
        if create_time_var:
            kwargs = {"have_date": val_var.have_date,
                      "have_time": val_var.have_time}
            attrs = [[TimeVariable(f"{v}", **kwargs) for v in vals]] * 2
            attrs.extend([[TimeVariable("Total", **kwargs)]] * 2)
        elif create_cont_var:
            attrs = [[ContinuousVariable(f"{v}", 1) for v in vals]] * 2
            attrs.extend([[ContinuousVariable("Total", 1)]] * 2)
        else:
            attrs = []
            for x in (X, X_h):
                attrs.append([DiscreteVariable(f"{v}", map_values(i, x))
                              for i, v in enumerate(vals, 2)])
            for x in (X_v, X_t):
                attrs.append([DiscreteVariable("Total", map_values(0, x))])
        row_var_h = DiscreteVariable(self._row_var.name, values=["Total"])
        aggr_attr = DiscreteVariable('Aggregate', [str(f) for f in agg_funs])

        same_row_col = self._col_var is self._row_var

        extra_vars = [self._row_var, aggr_attr]
        uniq_a = get_unique_names_duplicates([v.name for v in extra_vars]
                                             + [atr.name for atr in attrs[0]])
        for (idx, var), u in zip(enumerate(chain(extra_vars, attrs[0])), uniq_a):
            if var.name == u:
                continue
            if idx == 0:
                self.renamed.append(self._row_var.name)
                self._row_var = self._row_var.copy(name=u)
                if same_row_col:
                    self._col_var = self._row_var
                row_var_h = row_var_h.copy(name=u)
            elif idx == 1:
                self.renamed.append(aggr_attr.name)
                aggr_attr = aggr_attr.copy(name=u)
            else:
                self.renamed.append(var.name)
                attrs[0][idx-2] = var.copy(name=u)
                attrs[1][idx-2] = var.copy(name=u)

        if same_row_col:
            vals = tuple(v.name for v in attrs[0])
            self._row_var.make(self._row_var.name, values=vals)
            vals = tuple(v.name for v in attrs[2])
            row_var_h.make(row_var_h.name, vals)

        return (Domain([self._row_var, aggr_attr] + attrs[0]),
                Domain([row_var_h, aggr_attr] + attrs[1]),
                Domain(attrs[2]), Domain(attrs[3]))

    def __get_pivot_tab_x(self, val_var, agg_funs):
        gt = self._group_tables
        n_fun = len(agg_funs)
        n_rows, n_cols = len(self._row_var_groups), len(self._col_var_groups)
        is_float_type = not val_var or val_var.is_continuous
        if isinstance(val_var, TimeVariable):
            is_float_type = \
                all(fun in self.TimeVarFunctions for fun in agg_funs) or \
                all(fun in self.FloatFunctions for fun in agg_funs)
        kwargs = {"fill_value": np.nan, "dtype": float} if is_float_type \
            else {"fill_value": "", "dtype": object}
        X = np.full((n_rows * n_fun, 2 + n_cols), **kwargs)
        X_h = np.full((n_fun, 2 + n_cols), **kwargs)
        X_v = np.full((n_rows * n_fun, 1), **kwargs)
        X_t = np.full((n_fun, 1), **kwargs)
        for i, fun in enumerate(agg_funs):
            args = (val_var, fun, is_float_type)
            X[i::n_fun, 2:] = self.__rows_for_function(n_rows, n_cols, *args)
            X[i::n_fun, :2] = np.array([[row_val, agg_funs.index(fun)] for
                                        row_val in self._row_var_groups])
            X_h[i, :2] = 0, agg_funs.index(fun)
            X_h[i, 2:] = self.__total_for_function(gt.total_h, *args)
            X_v[i::n_fun, 0] = self.__total_for_function(gt.total_v, *args)
            X_t[i] = self.__total_for_function(gt.total, *args)
        return X, X_h, X_v, X_t

    def __total_for_function(self, group_tab, val_var, fun, is_float_type):
        ref = self._indepen_agg_done.get(fun, None) \
              or self._depen_agg_done[fun][val_var]
        ref -= int(bool(not self.single_var_grouping))
        return self.__check_continuous(val_var, group_tab.X[:, ref],
                                       fun, is_float_type)

    def __rows_for_function(self, n_rows, n_cols, val_var, fun, is_float_type):
        ref = self._indepen_agg_done.get(fun, None) \
              or self._depen_agg_done[fun][val_var]
        column = self._group_tables.table.X[:, ref]
        if self.single_var_grouping:
            rows = np.full((n_rows, n_cols), fun(np.array([]), ), dtype=float)
            rows[np.diag_indices_from(rows)] = column
        else:
            rows = column.reshape(n_rows, n_cols)
        return self.__check_continuous(val_var, rows, fun, is_float_type)

    def __check_continuous(self, val_var, column, fun, is_float_type):
        if val_var and not val_var.is_continuous:
            column = column.astype(str)
            if fun in self.DiscVarFunctions:
                for j, val in enumerate(val_var.values):
                    column[column == str(float(j))] = val
        elif isinstance(val_var, TimeVariable) and not is_float_type:
            shape = column.shape
            column = column.flatten()
            column_ = column.astype(str)
            if fun in self.TimeVarFunctions:
                for i in range(column.shape[0]):
                    if not np.isnan(column[i]):
                        column_[i] = val_var.repr_val(column[i])
            return column_.reshape(shape)
        return column

    @staticmethod
    def count_defined(x):
        if x.shape[0] == 0:
            return 0
        if x.size and np.issubdtype(x.dtype, np.number) and not sp.issparse(x):
            nans = np.isnan(x).sum(axis=0)
        elif sp.issparse(x) and x.size:
            nans = np.bincount(x.nonzero()[1], minlength=x.shape[1])
            x = x.tocsc()
        else:
            x_str = x.astype(str)
            nans = ((x_str == "nan") | (x_str == "")).sum(axis=0) \
                if x.size else np.zeros(x.shape[1])
        return x.shape[0] - nans

    @staticmethod
    def stat(x, f):
        return f(x.astype(np.float), axis=0) if x.shape[0] > 0 else np.nan

    @staticmethod
    def mode(x):
        return Pivot.stat(x, nanmode).mode if x.shape[0] > 0 else np.nan

    @staticmethod
    def majority(x):
        if x.shape[0] == 0:
            return np.nan
        counts = bincount(x)[0]
        return np.argmax(counts) if counts.shape[0] else np.nan

    Count.func = lambda x: len(x[0])
    Count_defined.func = lambda x: Pivot.count_defined(x[0])
    Sum.func = lambda x: nansum(x[0], axis=0) if x[0].shape[0] > 0 else 0
    Mean.func = lambda x: Pivot.stat(x[0], nanmean)
    Min.func = lambda x: Pivot.stat(x[0], nanmin)
    Max.func = lambda x: Pivot.stat(x[0], nanmax)
    Median.func = lambda x: Pivot.stat(x[0], nanmedian)
    Mode.func = lambda x: Pivot.mode(x[0])
    Var.func = lambda x: Pivot.stat(x[0], nanvar)
    Majority.func = lambda x: Pivot.majority(x[0])


class BorderedItemDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        """Overloads `paint` to draw borders"""
        QStyledItemDelegate.paint(self, painter, option, index)
        if index.data(BorderRole):
            painter.save()
            painter.setPen(index.data(BorderColorRole))
            rect = option.rect
            painter.drawLine(rect.topLeft(), rect.topRight())
            painter.restore()


class PivotTableView(QTableView):
    selection_changed = pyqtSignal()

    TOTAL_STRING = "Total"

    def __init__(self):
        super().__init__(editTriggers=QTableView.NoEditTriggers)
        self._n_classesv = None  # number of row_feature values
        self._n_classesh = None  # number of col_feature values
        self._n_agg_func = None  # number of aggregation functions
        self._n_leading_rows = None  # number of leading rows
        self._n_leading_cols = None  # number of leading columns

        self.table_model = QStandardItemModel(self)
        self.setModel(self.table_model)
        self.horizontalHeader().hide()
        self.verticalHeader().hide()
        self.horizontalHeader().setMinimumSectionSize(60)
        self.setShowGrid(False)
        self.setSizePolicy(QSizePolicy.MinimumExpanding,
                           QSizePolicy.MinimumExpanding)
        self.setItemDelegate(BorderedItemDelegate())
        self.pressed.connect(self.__cell_clicked)
        self.clicked.connect(self.__cell_clicked)
        self.entered.connect(self.__cell_entered)
        self.__clicked_cell = None

    @property
    def add_agg_column(self) -> bool:
        return self._n_agg_func > 1

    def __cell_entered(self, model_index):
        if self.__clicked_cell is None:
            return
        index = self.table_model.index
        selection = None
        i_end, j_end = model_index.row(), model_index.column()
        i_start, j_start = self.__clicked_cell
        i_start, i_end = sorted([i_start, i_end])
        j_start, j_end = sorted([j_start, j_end])
        if i_start >= self._n_leading_rows and j_start >= self._n_leading_cols:
            i_start = (i_start - self._n_leading_rows) // self._n_agg_func * \
                self._n_agg_func + self._n_leading_rows
            i_end = (i_end - self._n_leading_rows) // self._n_agg_func * \
                self._n_agg_func + self._n_leading_rows + self._n_agg_func - 1
            start, end = index(i_start, j_start), index(i_end, j_end)
            selection = QItemSelection(start, end)
        if selection is not None:
            self.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)
        self.selection_changed.emit()

    def __cell_clicked(self, model_index):
        i, j = model_index.row(), model_index.column()
        self.__clicked_cell = (i, j)
        m, n = self.table_model.rowCount(), self.table_model.columnCount()
        index = self.table_model.index
        selection = None
        if i > m - self._n_agg_func - 1 and j == n - 1:
            start_index = index(self._n_leading_rows, self._n_leading_cols)
            selection = QItemSelection(start_index, index(m - 1, n - 1))
        elif i == self._n_leading_rows - 1 or i > m - self._n_agg_func - 1:
            start_index = index(self._n_leading_rows, j)
            selection = QItemSelection(start_index, index(m - 1, j))
        elif j in (self._n_leading_cols - 1, n - 1, 1):
            i_start = (i - self._n_leading_rows) // self._n_agg_func * \
                      self._n_agg_func + self._n_leading_rows
            i_end = i_start + self._n_agg_func - 1
            start_index = index(i_start, self._n_leading_cols)
            selection = QItemSelection(start_index, index(i_end, n - 1))
        elif i >= self._n_leading_rows and j >= self._n_leading_cols:
            i_start = (i - self._n_leading_rows) // self._n_agg_func * \
                      self._n_agg_func + self._n_leading_rows
            i_end = i_start + self._n_agg_func - 1
            selection = QItemSelection(index(i_start, j), index(i_end, j))

        if selection is not None:
            self.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def mouseReleaseEvent(self, e):
        super().mouseReleaseEvent(e)
        self.selection_changed.emit()

    def update_table(self, titleh: str, titlev: str, table: Table,
                     table_total_h: Table, table_total_v: Table,
                     table_total: Table):
        self.clear()
        if not table:
            return

        self._initialize(table, table_total_h)
        self._set_headers(titleh, titlev, table)
        self._set_values(table[:, 2:])
        self._set_totals(table_total_h[:, 2:], table_total_v, table_total)
        self._draw_lines()
        self._resize(table)

    def _initialize(self, table, table_total_h):
        self._n_classesv = int(len(table) / len(table_total_h))
        self._n_classesh = table.X.shape[1] - 2
        self._n_agg_func = len(table_total_h)
        self._n_leading_rows = 2
        self._n_leading_cols = 2 + int(len(table_total_h) > 1)

    def _set_headers(self, titleh, titlev, table):
        self.__set_horizontal_title(titleh)
        self.__set_vertical_title(titlev)
        self.__set_flags_title()
        self.__set_horizontal_headers(table)
        self.__set_vertical_headers(table)

    def __set_horizontal_title(self, titleh):
        item = QStandardItem()
        item.setData(titleh, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        self.table_model.setItem(0, self._n_leading_cols, item)
        self.setSpan(0, self._n_leading_cols, 1, self._n_classesh + 3)

    def __set_vertical_title(self, titlev):
        item = QStandardItem()
        item.setData(titlev, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        self.setItemDelegateForColumn(0, gui.VerticalItemDelegate(extend=True))
        self.table_model.setItem(self._n_leading_rows, 0, item)
        row_span = self._n_classesv * self._n_agg_func + 1
        self.setSpan(self._n_leading_rows, 0, row_span, 1)

    def __set_flags_title(self):
        item = self.table_model.item(0, self._n_leading_cols)
        item.setFlags(Qt.NoItemFlags)
        item = self.table_model.item(self._n_leading_rows, 0)
        item.setFlags(Qt.NoItemFlags)
        for i, j in product(range(self._n_leading_rows),
                            range(self._n_leading_cols)):
            item = QStandardItem()
            item.setFlags(Qt.NoItemFlags)
            self.table_model.setItem(i, j, item)

    def __set_horizontal_headers(self, table):
        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]
        if not self.add_agg_column:
            labels[0] = str(table[0, 1])
        for i, label in enumerate(labels, self._n_leading_cols - 1):
            self.table_model.setItem(1, i, self._create_header_item(label))

    def __set_vertical_headers(self, table):
        labels = [(str(row[0]), str(row[1])) for row in table]
        i = self._n_leading_rows - 1
        for i, (l1, l2) in enumerate(labels, self._n_leading_rows):
            l1 = "" if (i - self._n_leading_rows) % self._n_agg_func else l1
            self.table_model.setItem(i, 1, self._create_header_item(l1))
            if self.add_agg_column:
                self.table_model.setItem(i, 2, self._create_header_item(l2))

        if self.add_agg_column:
            labels = [str(row[1]) for row in table[:self._n_agg_func]]
            start = self._n_leading_rows + self._n_agg_func * self._n_classesv
            for j, l2 in enumerate(labels, i + 1):
                l1 = self.TOTAL_STRING if j == start else ""
                self.table_model.setItem(j, 1, self._create_header_item(l1))
                self.table_model.setItem(j, 2, self._create_header_item(l2))
        else:
            item = self._create_header_item(self.TOTAL_STRING)
            self.table_model.setItem(i + 1, 1, item)

    def _set_values(self, table):
        for i, j in product(range(len(table)), range(len(table[0]))):
            value = table[i, j]
            item = self._create_value_item(str(value))
            self.table_model.setItem(i + self._n_leading_rows,
                                     j + self._n_leading_cols, item)

    def _set_totals(self, table_total_h, table_total_v, table_total):
        def set_total_item(table, get_row, get_col):
            for i, j in product(range(len(table)), range(len(table[0]))):
                item = self._create_header_item(str(table[i, j]))
                self.table_model.setItem(get_row(i), get_col(j), item)

        last_row = self._n_leading_rows + self._n_classesv * self._n_agg_func
        last_col = self._n_leading_cols + self._n_classesh
        set_total_item(table_total_v, lambda x: x + self._n_leading_rows,
                       lambda x: last_col)
        set_total_item(table_total_h, lambda x: x + last_row,
                       lambda x: x + self._n_leading_cols)
        set_total_item(table_total, lambda x: x + last_row, lambda x: last_col)

    def _create_header_item(self, text):
        bold_font = self.table_model.invisibleRootItem().font()
        bold_font.setBold(True)
        item = QStandardItem()
        item.setData(text, Qt.DisplayRole)
        item.setFont(bold_font)
        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
        item.setFlags(Qt.ItemIsEnabled)
        return item

    @staticmethod
    def _create_value_item(text):
        item = QStandardItem()
        item.setData(text, Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        return item

    def _draw_lines(self):
        end_col = self._n_leading_cols + self._n_classesh + 1
        total_row = self._n_leading_rows + self._n_classesv * self._n_agg_func
        indices = [(total_row, j) for j in range(1, end_col)]
        for i in range(self._n_classesv):
            inner_row = self._n_agg_func * i + self._n_leading_rows
            inner_indices = [(inner_row, j) for j in range(1, end_col)]
            indices = indices + inner_indices
            if not self.add_agg_column:
                break
        for i, j in indices:
            item = self.table_model.item(i, j)
            item.setData("t", BorderRole)
            item.setData(QColor(160, 160, 160), BorderColorRole)

    def _resize(self, table):
        labels = [a.name for a in table.domain[1:]] + [self.TOTAL_STRING]
        if len(' '.join(labels)) < 120:
            self.horizontalHeader().setSectionResizeMode(
                QHeaderView.ResizeToContents)
        else:
            self.horizontalHeader().setDefaultSectionSize(60)

    def get_selection(self) -> Set:
        m, n = self._n_leading_rows, self._n_leading_cols
        return {(ind.row() - m, ind.column() - n)
                for ind in self.selectedIndexes()}

    def set_selection(self, indexes: Set):
        selection = QItemSelection()
        index = self.model().index
        for row, col in indexes:
            sel = index(row + self._n_leading_rows, col + self._n_leading_cols)
            selection.select(sel, sel)
        self.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def clear(self):
        self.table_model.clear()


class OWPivot(OWWidget):
    name = "Pivot Table"
    description = "Reshape data table based on column values."
    icon = "icons/Pivot.svg"
    priority = 1000
    keywords = ["pivot", "group", "aggregate"]

    class Inputs:
        data = Input("Data", Table, default=True)

    class Outputs:
        pivot_table = Output("Pivot Table", Table, default=True)
        filtered_data = Output("Filtered Data", Table)
        grouped_data = Output("Grouped Data", Table)

    class Warning(OWWidget.Warning):
        # TODO - inconsistent for different variable types
        no_col_feature = Msg("Column feature should be selected.")
        cannot_aggregate = Msg("Some aggregations ({}) cannot be computed.")
        renamed_vars = Msg("Some variables have been renamed in some tables"
                           "to avoid duplicates.\n{}")
        too_many_values = Msg("Selected variable has too many values.")
        no_variables = Msg("At least 1 primitive variable is required.")

    settingsHandler = DomainContextHandler()
    row_feature = ContextSetting(None)
    col_feature = ContextSetting(None)
    val_feature = ContextSetting(None)
    sel_agg_functions = Setting(set([Pivot.Count]))
    selection = Setting(set(), schema_only=True)
    auto_commit = Setting(True)

    AGGREGATIONS = (Pivot.Count,
                    Pivot.Count_defined,
                    None,  # separator
                    Pivot.Sum,
                    Pivot.Mean,
                    Pivot.Var,
                    Pivot.Median,
                    2,  # column break
                    Pivot.Mode,
                    Pivot.Min,
                    Pivot.Max,
                    None,
                    Pivot.Majority)

    MAX_VALUES = 100

    def __init__(self):
        super().__init__()
        self.data = None  # type: Table
        self.pivot = None  # type: Pivot
        self.__pending_selection = self.selection  # type: Set
        self._add_control_area_controls()
        self._add_main_area_controls()

    def _add_control_area_controls(self):
        gui.comboBox(gui.vBox(self.controlArea, box="Rows"),
                     self, "row_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(valid_types=DomainModel.PRIMITIVE),
                     callback=self.__feature_changed,
                     orientation=Qt.Horizontal)
        gui.comboBox(gui.vBox(self.controlArea, box="Columns"),
                     self, "col_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(placeholder="(Same as rows)",
                                       valid_types=DiscreteVariable),
                     callback=self.__feature_changed,
                     orientation=Qt.Horizontal)
        gui.comboBox(gui.vBox(self.controlArea, box="Values"),
                     self, "val_feature",
                     contentsLength=14,
                     searchable=True,
                     model=DomainModel(placeholder="(None)"),
                     callback=self.__val_feature_changed,
                     orientation=Qt.Horizontal)
        self.__add_aggregation_controls()
        gui.rubber(self.controlArea)
        gui.auto_apply(self.buttonsArea, self, "auto_commit")

    def __add_aggregation_controls(self):
        def new_inbox():
            nonlocal row, col, inbox
            inbox = QWidget()
            layout = QGridLayout()
            inbox.setLayout(layout)
            layout.setContentsMargins(0, 0, 0, 0)
            box.layout().addWidget(inbox)
            row = col = 0

        box = gui.vBox(self.controlArea, "Aggregations")
        row = col = 0
        inbox = None
        new_inbox()
        self.aggregation_checkboxes = []  # for test purposes
        for agg in self.AGGREGATIONS:
            if agg is None:
                line = QFrame()
                line.setFrameShape(QFrame.HLine)
                line.setLineWidth(1)
                line.setFrameShadow(QFrame.Sunken)
                box.layout().addWidget(line)
                new_inbox()
                continue
            elif agg == 2:
                col += 1
                row = 0
                continue
            check_box = QCheckBox(str(agg), inbox)
            check_box.setChecked(agg in self.sel_agg_functions)
            check_box.clicked.connect(lambda *args, a=agg:
                                      self.__aggregation_cb_clicked(a, args[0]))
            inbox.layout().addWidget(check_box, row, col)
            self.aggregation_checkboxes.append(check_box)
            row += 1

    def _add_main_area_controls(self):
        self.table_view = PivotTableView()
        self.table_view.selection_changed.connect(self.__invalidate_filtered)
        self.mainArea.layout().addWidget(self.table_view)

    @property
    def no_col_feature(self):
        return self.col_feature is None and self.row_feature is not None \
            and self.row_feature.is_continuous

    @property
    def skipped_aggs(self):
        def add(fun):
            data, var = self.data, self.val_feature
            return data and not var and fun not in Pivot.AutonomousFunctions \
                or var and var.is_discrete and fun in Pivot.ContVarFunctions \
                or var and var.is_continuous and fun in Pivot.DiscVarFunctions
        skipped = [str(fun) for fun in self.sel_agg_functions if add(fun)]
        return ", ".join(sorted(skipped))

    @property
    def data_has_primitives(self):
        if not self.data:
            return False
        domain = self.data.domain
        return any(v.is_primitive() for v in domain.variables + domain.metas)

    def __feature_changed(self):
        self.selection = set()
        self.pivot = None
        self.commit()

    def __val_feature_changed(self):
        self.selection = set()
        if self.no_col_feature or not self.pivot:
            return
        self.pivot.update_pivot_table(self.val_feature)
        self.commit()

    def __aggregation_cb_clicked(self, agg_fun: Pivot.Functions, checked: bool):
        self.selection = set()
        if checked:
            self.sel_agg_functions.add(agg_fun)
        else:
            self.sel_agg_functions.remove(agg_fun)
        if self.no_col_feature or not self.pivot or not self.data:
            return
        self.pivot.update_group_table(self.sel_agg_functions, self.val_feature)
        self.commit()

    def __invalidate_filtered(self):
        self.selection = self.table_view.get_selection()
        self.commit()

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.selection = set()
        self.data = data
        self.pivot = None
        self.check_data()
        self.init_attr_values()
        if self.data_has_primitives:
            self.openContext(self.data)
        self.unconditional_commit()

    def check_data(self):
        self.clear_messages()

    def init_attr_values(self):
        domain = self.data.domain if self.data and len(self.data) else None
        for attr in ("row_feature", "col_feature", "val_feature"):
            getattr(self.controls, attr).model().set_domain(domain)
            setattr(self, attr, None)
        model = self.controls.row_feature.model()
        if model:
            self.row_feature = model[0]
        model = self.controls.val_feature.model()
        if model and len(model) > 2:
            self.val_feature = domain.variables[0] \
                if domain.variables[0] in model else model[2]

    def commit(self):
        def send_outputs(pivot_table, filtered_data, grouped_data):
            if self.data:
                if grouped_data:
                    grouped_data.name = self.data.name
                if pivot_table:
                    pivot_table.name = self.data.name
                if filtered_data:
                    filtered_data.name = self.data.name
            self.Outputs.grouped_data.send(grouped_data)
            self.Outputs.pivot_table.send(pivot_table)
            self.Outputs.filtered_data.send(filtered_data)

        self.Warning.renamed_vars.clear()
        self.Warning.too_many_values.clear()
        self.Warning.cannot_aggregate.clear()
        self.Warning.no_col_feature.clear()

        self.table_view.clear()

        if self.pivot is None:
            if self.data:
                if not self.data_has_primitives:
                    self.Warning.no_variables()
                    send_outputs(None, None, None)
                    return

            if self.no_col_feature:
                self.Warning.no_col_feature()
                send_outputs(None, None, None)
                return

            if self.data:
                col_var = self.col_feature or self.row_feature
                col = self.data.get_column_view(col_var)[0].astype(np.float)
                if len(nanunique(col)) >= self.MAX_VALUES:
                    self.table_view.clear()
                    self.Warning.too_many_values()
                    send_outputs(None, None, None)
                    return

            self.pivot = Pivot(self.data, self.sel_agg_functions,
                               self.row_feature,
                               self.col_feature, self.val_feature)

        if self.skipped_aggs:
            self.Warning.cannot_aggregate(self.skipped_aggs)
        self._update_graph()

        send_outputs(self.pivot.pivot_table,
                     self.get_filtered_data(),
                     self.pivot.group_table)

        if self.pivot.renamed:
            self.Warning.renamed_vars(self.pivot.renamed)

    def _update_graph(self):
        if self.pivot.pivot_table:
            col_feature = self.col_feature or self.row_feature
            self.table_view.update_table(col_feature.name,
                                         self.row_feature.name,
                                         *self.pivot.pivot_tables)
            selection = self.__pending_selection or self.selection
            self.table_view.set_selection(selection)
            self.selection = self.table_view.get_selection()
            self.__pending_selection = set()

    def get_filtered_data(self):
        if not self.data or not self.selection or not self.pivot.pivot_table:
            return None

        cond = []
        for i, j in self.selection:
            f = []
            for at, val in [(self.row_feature, self.pivot.pivot_table.X[i, 0]),
                            (self.col_feature, j)]:
                if isinstance(at, DiscreteVariable):
                    f.append(FilterDiscrete(at, [val]))
                elif isinstance(at, ContinuousVariable):
                    f.append(FilterContinuous(at, FilterContinuous.Equal, val))
            cond.append(Values(f))
        return Values(cond, conjunction=False)(self.data)

    @staticmethod
    def sizeHint():
        return QSize(640, 525)

    def send_report(self):
        self.report_items((
            ("Row feature", self.row_feature),
            ("Column feature", self.col_feature),
            ("Value feature", self.val_feature)))
        if self.data and self.val_feature is not None:
            self.report_table("", self.table_view)
        if not self.data:
            self.report_items((("Group by", self.row_feature),))
            self.report_table(self.table_view)


if __name__ == "__main__":
    WidgetPreview(OWPivot).run(set_data=Table("heart_disease"))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user