{
    "file_diffs": [
        {
            "old_file_content": "import enum\nfrom collections import OrderedDict\nfrom datetime import datetime, timezone, timedelta\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,\n    QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,\n    QLabel, QHBoxLayout, QDateTimeEdit, QCalendarWidget)\nfrom AnyQt.QtGui import (QDoubleValidator, QStandardItemModel, QStandardItem,\n                         QFontMetrics, QPalette)\nfrom AnyQt.QtCore import Qt, QPoint, QPersistentModelIndex, QLocale, \\\n    QDateTime, QDate, QTime\n\nfrom orangewidget.utils.combobox import ComboBoxSearch\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.data import (\n    ContinuousVariable, DiscreteVariable, StringVariable, TimeVariable, Table)\nimport Orange.data.filter as data_filter\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.data.sql.table import SqlTable\nfrom Orange.preprocess import Remove\nfrom Orange.widgets import widget, gui\nfrom Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\nfrom Orange.widgets.utils import vartype\nfrom Orange.widgets import report\nfrom Orange.widgets.widget import Msg\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.state_summary import format_summary_details\n\n\nclass SelectRowsContextHandler(DomainContextHandler):\n    \"\"\"Context handler that filters conditions\"\"\"\n\n    # pylint: disable=arguments-differ\n    def is_valid_item(self, setting, condition, attrs, metas):\n        \"\"\"Return True if condition applies to a variable in given domain.\"\"\"\n        varname, *_ = condition\n        return varname in attrs or varname in metas\n\n    def encode_setting(self, context, setting, value):\n        if setting.name != 'conditions':\n            return super().encode_settings(context, setting, value)\n\n        encoded = []\n        CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n        for attr, op, values in value:\n            if isinstance(attr, str):\n                if OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                # None will match the value returned by all_vars.get\n                encoded.append((attr, None, op, values))\n            else:\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        and values and isinstance(values[0], str):\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                elif isinstance(attr, DiscreteVariable):\n                    values = [attr.values[i - 1] if i else \"\" for i in values]\n                encoded.append((\n                    attr.name,\n                    context.attributes.get(attr.name)\n                    or context.metas.get(attr.name),\n                    op,\n                    values\n                ))\n        return encoded\n\n    # pylint: disable=arguments-differ\n    def decode_setting(self, setting, value, domain=None, *_args):\n        value = super().decode_setting(setting, value, domain)\n        if setting.name == 'conditions':\n            CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n            # Use this after 2022/2/2:\n            # for i, (attr, tpe, op, values) in enumerate(value):\n            #     if tpe is not None:\n            for i, (attr, *tpe, op, values) in enumerate(value):\n                if tpe != [None] \\\n                        or not tpe and attr not in OWSelectRows.AllTypes:\n                    attr = domain[attr]\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        or OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toString(float(i), 'f') for i in values]\n                elif isinstance(attr, DiscreteVariable):\n                    # After 2022/2/2, use just the expression in else clause\n                    if values and isinstance(values[0], int):\n                        # Backwards compatibility. Reset setting if we detect\n                        # that the number of values decreased. Still broken if\n                        # they're reordered or we don't detect the decrease.\n                        #\n                        # indices start with 1, thus >, not >=\n                        if max(values) > len(attr.values):\n                            values = (0, )\n                    else:\n                        values = tuple(attr.to_val(val) + 1 if val else 0\n                                       for val in values if val in attr.values) \\\n                                 or (0, )\n                value[i] = (attr, op, values)\n        return value\n\n    def match(self, context, domain, attrs, metas):\n        if (attrs, metas) == (context.attributes, context.metas):\n            return self.PERFECT_MATCH\n\n        conditions = context.values[\"conditions\"]\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        matched = [all_vars.get(name) == tpe  # also matches \"all (...)\" strings\n                   # After 2022/2/2 remove this line:\n                   if len(rest) == 2 else name in all_vars\n                   for name, tpe, *rest in conditions]\n        if any(matched):\n            return 0.5 * sum(matched) / len(matched)\n        return self.NO_MATCH\n\n    def filter_value(self, setting, data, domain, attrs, metas):\n        if setting.name != \"conditions\":\n            super().filter_value(setting, data, domain, attrs, metas)\n            return\n\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        conditions = data[\"conditions\"]\n        # Use this after 2022/2/2: if any(all_vars.get(name) == tpe:\n        # conditions[:] = [(name, tpe, *rest) for name, tpe, *rest in conditions\n        #                  if all_vars.get(name) == tpe]\n        conditions[:] = [\n            (name, tpe, *rest) for name, tpe, *rest in conditions\n            # all_vars.get(name) == tpe also matches \"all (...)\" which are\n            # encoded with type `None`\n            if (all_vars.get(name) == tpe if len(rest) == 2\n                else name in all_vars)]\n\n\nclass FilterDiscreteType(enum.Enum):\n    # pylint: disable=invalid-name\n    Equal = \"Equal\"\n    NotEqual = \"NotEqual\"\n    In = \"In\"\n    IsDefined = \"IsDefined\"\n\n\ndef _plural(s):\n    s = s.replace(\"is \", \"are \")\n    for word in (\"equals\", \"contains\", \"begins\", \"ends\"):\n        s = s.replace(word, word[:-1])\n    return s\n\n\nclass CalendarWidgetWithTime(QCalendarWidget):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.timeedit = QDateTimeEdit(displayFormat=\"hh:mm:ss\")\n        self.timeedit.setTime(self.parent().min_datetime.time())\n\n        self._time_layout = sublay = QHBoxLayout()\n        sublay.setContentsMargins(6, 6, 6, 6)\n        sublay.addStretch(1)\n        sublay.addWidget(QLabel(\"Time: \"))\n        sublay.addWidget(self.timeedit)\n        sublay.addStretch(1)\n        self.layout().addLayout(sublay)\n\n    def minimumSize(self):\n        return self.sizeHint()\n\n    def sizeHint(self):\n        size = super().sizeHint()\n        size.setHeight(\n            size.height()\n            + self._time_layout.sizeHint().height()\n            + self.layout().spacing())\n        return size\n\nclass OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('×', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data[:, var_idx]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is {valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name} {attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name} {' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))\n\n    # Uncomment this on 2022/2/2\n    #\n    # @classmethod\n    # def migrate_context(cls, context, version):\n    #     if not version or version < 2:\n    #         # Just remove; can't migrate because variables types are unknown\n    #         context.values[\"conditions\"] = []\n\n\nclass CheckBoxPopup(QWidget):\n    def __init__(self, var, lc, widget_parent=None, widg=None):\n        QWidget.__init__(self)\n\n        self.list_view = QListView()\n        text = []\n        model = QStandardItemModel(self.list_view)\n        for (i, val) in enumerate(var.values):\n            item = QStandardItem(val)\n            item.setCheckable(True)\n            if i + 1 in lc:\n                item.setCheckState(Qt.Checked)\n                text.append(val)\n            model.appendRow(item)\n        model.itemChanged.connect(widget_parent.conditions_changed)\n        self.list_view.setModel(model)\n\n        layout = QGridLayout(self)\n        layout.addWidget(self.list_view)\n        layout.setContentsMargins(0, 0, 0, 0)\n        self.setLayout(layout)\n\n        self.adjustSize()\n        self.setWindowFlags(Qt.Popup)\n\n        self.widget = widg\n        self.widget.desc_text = ', '.join(text)\n        self.widget.set_text()\n\n    def moved(self):\n        point = self.widget.rect().bottomRight()\n        global_point = self.widget.mapToGlobal(point)\n        self.move(global_point - QPoint(self.width(), 0))\n\n\nclass DropDownToolButton(QToolButton):\n    def __init__(self, parent, var, lc):\n        QToolButton.__init__(self, parent)\n        self.desc_text = ''\n        self.popup = CheckBoxPopup(var, lc, parent, self)\n        self.setMenu(QMenu()) # to show arrow\n        self.clicked.connect(self.open_popup)\n\n    def open_popup(self):\n        self.popup.moved()\n        self.popup.show()\n\n    def set_text(self):\n        metrics = QFontMetrics(self.font())\n        self.setText(metrics.elidedText(self.desc_text, Qt.ElideRight,\n                                        self.width() - 15))\n\n    def resizeEvent(self, _):\n        self.set_text()\n\n\nclass DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        item_list = [item for items in list(column) for item in items]\n        min_datetime = convert_timestamp(\n            np.nanmin(item_list)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(item_list)).strftime(convert_format)\n        return min_datetime, max_datetime\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWSelectRows).run(Table(\"heart_disease\"))\n",
            "new_file_content": "import enum\nfrom collections import OrderedDict\nfrom datetime import datetime, timezone, timedelta\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,\n    QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,\n    QLabel, QHBoxLayout, QDateTimeEdit, QCalendarWidget)\nfrom AnyQt.QtGui import (QDoubleValidator, QStandardItemModel, QStandardItem,\n                         QFontMetrics, QPalette)\nfrom AnyQt.QtCore import Qt, QPoint, QPersistentModelIndex, QLocale, \\\n    QDateTime, QDate, QTime\n\nfrom orangewidget.utils.combobox import ComboBoxSearch\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.data import (\n    ContinuousVariable, DiscreteVariable, StringVariable, TimeVariable, Table)\nimport Orange.data.filter as data_filter\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.data.sql.table import SqlTable\nfrom Orange.preprocess import Remove\nfrom Orange.widgets import widget, gui\nfrom Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\nfrom Orange.widgets.utils import vartype\nfrom Orange.widgets import report\nfrom Orange.widgets.widget import Msg\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.state_summary import format_summary_details\n\n\nclass SelectRowsContextHandler(DomainContextHandler):\n    \"\"\"Context handler that filters conditions\"\"\"\n\n    # pylint: disable=arguments-differ\n    def is_valid_item(self, setting, condition, attrs, metas):\n        \"\"\"Return True if condition applies to a variable in given domain.\"\"\"\n        varname, *_ = condition\n        return varname in attrs or varname in metas\n\n    def encode_setting(self, context, setting, value):\n        if setting.name != 'conditions':\n            return super().encode_settings(context, setting, value)\n\n        encoded = []\n        CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n        for attr, op, values in value:\n            if isinstance(attr, str):\n                if OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                # None will match the value returned by all_vars.get\n                encoded.append((attr, None, op, values))\n            else:\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        and values and isinstance(values[0], str):\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                elif isinstance(attr, DiscreteVariable):\n                    values = [attr.values[i - 1] if i else \"\" for i in values]\n                encoded.append((\n                    attr.name,\n                    context.attributes.get(attr.name)\n                    or context.metas.get(attr.name),\n                    op,\n                    values\n                ))\n        return encoded\n\n    # pylint: disable=arguments-differ\n    def decode_setting(self, setting, value, domain=None, *_args):\n        value = super().decode_setting(setting, value, domain)\n        if setting.name == 'conditions':\n            CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n            # Use this after 2022/2/2:\n            # for i, (attr, tpe, op, values) in enumerate(value):\n            #     if tpe is not None:\n            for i, (attr, *tpe, op, values) in enumerate(value):\n                if tpe != [None] \\\n                        or not tpe and attr not in OWSelectRows.AllTypes:\n                    attr = domain[attr]\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        or OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toString(float(i), 'f') for i in values]\n                elif isinstance(attr, DiscreteVariable):\n                    # After 2022/2/2, use just the expression in else clause\n                    if values and isinstance(values[0], int):\n                        # Backwards compatibility. Reset setting if we detect\n                        # that the number of values decreased. Still broken if\n                        # they're reordered or we don't detect the decrease.\n                        #\n                        # indices start with 1, thus >, not >=\n                        if max(values) > len(attr.values):\n                            values = (0, )\n                    else:\n                        values = tuple(attr.to_val(val) + 1 if val else 0\n                                       for val in values if val in attr.values) \\\n                                 or (0, )\n                value[i] = (attr, op, values)\n        return value\n\n    def match(self, context, domain, attrs, metas):\n        if (attrs, metas) == (context.attributes, context.metas):\n            return self.PERFECT_MATCH\n\n        conditions = context.values[\"conditions\"]\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        matched = [all_vars.get(name) == tpe  # also matches \"all (...)\" strings\n                   # After 2022/2/2 remove this line:\n                   if len(rest) == 2 else name in all_vars\n                   for name, tpe, *rest in conditions]\n        if any(matched):\n            return 0.5 * sum(matched) / len(matched)\n        return self.NO_MATCH\n\n    def filter_value(self, setting, data, domain, attrs, metas):\n        if setting.name != \"conditions\":\n            super().filter_value(setting, data, domain, attrs, metas)\n            return\n\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        conditions = data[\"conditions\"]\n        # Use this after 2022/2/2: if any(all_vars.get(name) == tpe:\n        # conditions[:] = [(name, tpe, *rest) for name, tpe, *rest in conditions\n        #                  if all_vars.get(name) == tpe]\n        conditions[:] = [\n            (name, tpe, *rest) for name, tpe, *rest in conditions\n            # all_vars.get(name) == tpe also matches \"all (...)\" which are\n            # encoded with type `None`\n            if (all_vars.get(name) == tpe if len(rest) == 2\n                else name in all_vars)]\n\n\nclass FilterDiscreteType(enum.Enum):\n    # pylint: disable=invalid-name\n    Equal = \"Equal\"\n    NotEqual = \"NotEqual\"\n    In = \"In\"\n    IsDefined = \"IsDefined\"\n\n\ndef _plural(s):\n    s = s.replace(\"is \", \"are \")\n    for word in (\"equals\", \"contains\", \"begins\", \"ends\"):\n        s = s.replace(word, word[:-1])\n    return s\n\n\nclass CalendarWidgetWithTime(QCalendarWidget):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.timeedit = QDateTimeEdit(displayFormat=\"hh:mm:ss\")\n        self.timeedit.setTime(self.parent().min_datetime.time())\n\n        self._time_layout = sublay = QHBoxLayout()\n        sublay.setContentsMargins(6, 6, 6, 6)\n        sublay.addStretch(1)\n        sublay.addWidget(QLabel(\"Time: \"))\n        sublay.addWidget(self.timeedit)\n        sublay.addStretch(1)\n        self.layout().addLayout(sublay)\n\n    def minimumSize(self):\n        return self.sizeHint()\n\n    def sizeHint(self):\n        size = super().sizeHint()\n        size.setHeight(\n            size.height()\n            + self._time_layout.sizeHint().height()\n            + self.layout().spacing())\n        return size\n\nclass OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('×', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data.get_column_view(var_idx)[0]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is {valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name} {attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name} {' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))\n\n    # Uncomment this on 2022/2/2\n    #\n    # @classmethod\n    # def migrate_context(cls, context, version):\n    #     if not version or version < 2:\n    #         # Just remove; can't migrate because variables types are unknown\n    #         context.values[\"conditions\"] = []\n\n\nclass CheckBoxPopup(QWidget):\n    def __init__(self, var, lc, widget_parent=None, widg=None):\n        QWidget.__init__(self)\n\n        self.list_view = QListView()\n        text = []\n        model = QStandardItemModel(self.list_view)\n        for (i, val) in enumerate(var.values):\n            item = QStandardItem(val)\n            item.setCheckable(True)\n            if i + 1 in lc:\n                item.setCheckState(Qt.Checked)\n                text.append(val)\n            model.appendRow(item)\n        model.itemChanged.connect(widget_parent.conditions_changed)\n        self.list_view.setModel(model)\n\n        layout = QGridLayout(self)\n        layout.addWidget(self.list_view)\n        layout.setContentsMargins(0, 0, 0, 0)\n        self.setLayout(layout)\n\n        self.adjustSize()\n        self.setWindowFlags(Qt.Popup)\n\n        self.widget = widg\n        self.widget.desc_text = ', '.join(text)\n        self.widget.set_text()\n\n    def moved(self):\n        point = self.widget.rect().bottomRight()\n        global_point = self.widget.mapToGlobal(point)\n        self.move(global_point - QPoint(self.width(), 0))\n\n\nclass DropDownToolButton(QToolButton):\n    def __init__(self, parent, var, lc):\n        QToolButton.__init__(self, parent)\n        self.desc_text = ''\n        self.popup = CheckBoxPopup(var, lc, parent, self)\n        self.setMenu(QMenu()) # to show arrow\n        self.clicked.connect(self.open_popup)\n\n    def open_popup(self):\n        self.popup.moved()\n        self.popup.show()\n\n    def set_text(self):\n        metrics = QFontMetrics(self.font())\n        self.setText(metrics.elidedText(self.desc_text, Qt.ElideRight,\n                                        self.width() - 15))\n\n    def resizeEvent(self, _):\n        self.set_text()\n\n\nclass DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        min_datetime = convert_timestamp(\n            np.nanmin(column)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(column)).strftime(convert_format)\n        return min_datetime, max_datetime\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWSelectRows).run(Table(\"heart_disease\"))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/data/owselectrows.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "124b55ce6",
                "new_commit_hash": "8abb49f47",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/data/owselectrows.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/data/owselectrows.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 554,
                            "length": 7
                        },
                        "new_range": {
                            "start": 554,
                            "length": 7
                        },
                        "section": "class OWSelectRows(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                    invalidate_datetime()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "                datetime_format = (var.have_date, var.have_time)",
                                "type": "context"
                            },
                            {
                                "content": "                column = self.data[:, var_idx]",
                                "type": "deleted"
                            },
                            {
                                "content": "                column = self.data.get_column_view(var_idx)[0]",
                                "type": "added"
                            },
                            {
                                "content": "                w = DateTimeWidget(self, column, datetime_format)",
                                "type": "context"
                            },
                            {
                                "content": "                w.set_datetime(lc[0])",
                                "type": "context"
                            },
                            {
                                "content": "                box.controls = [w]",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/owselectrows.py",
                            "type": "class",
                            "name": "OWSelectRows",
                            "content": "class OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('×', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data[:, var_idx]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is {valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name} {attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name} {' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 180,
                            "end_lineno": 825,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/data/owselectrows.py",
                            "type": "method",
                            "name": "OWSelectRows.set_new_values",
                            "content": "class OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('×', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data[:, var_idx]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is {valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name} {attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name} {' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 468,
                            "end_lineno": 576,
                            "parent": {
                                "file_name": "Orange/widgets/data/owselectrows.py",
                                "type": "class",
                                "name": "OWSelectRows",
                                "content": "class OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('×', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data[:, var_idx]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is {valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name} {attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name} {' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name} {' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 180,
                                "end_lineno": 825,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 951,
                            "length": 11
                        },
                        "new_range": {
                            "start": 951,
                            "length": 10
                        },
                        "section": "class DateTimeWidget(QDateTimeEdit):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\",
                                "type": "context"
                            },
                            {
                                "content": "                       timedelta(seconds=int(timestamp))",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        item_list = [item for items in list(column) for item in items]",
                                "type": "deleted"
                            },
                            {
                                "content": "        min_datetime = convert_timestamp(",
                                "type": "context"
                            },
                            {
                                "content": "            np.nanmin(item_list)).strftime(convert_format)",
                                "type": "deleted"
                            },
                            {
                                "content": "            np.nanmin(column)).strftime(convert_format)",
                                "type": "added"
                            },
                            {
                                "content": "        max_datetime = convert_timestamp(",
                                "type": "context"
                            },
                            {
                                "content": "            np.nanmax(item_list)).strftime(convert_format)",
                                "type": "deleted"
                            },
                            {
                                "content": "            np.nanmax(column)).strftime(convert_format)",
                                "type": "added"
                            },
                            {
                                "content": "        return min_datetime, max_datetime",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/owselectrows.py",
                            "type": "method",
                            "name": "DateTimeWidget.find_range",
                            "content": "class DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        item_list = [item for items in list(column) for item in items]\n        min_datetime = convert_timestamp(\n            np.nanmin(item_list)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(item_list)).strftime(convert_format)\n        return min_datetime, max_datetime",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 947,
                            "end_lineno": 959,
                            "parent": {
                                "file_name": "Orange/widgets/data/owselectrows.py",
                                "type": "class",
                                "name": "DateTimeWidget",
                                "content": "class DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        item_list = [item for items in list(column) for item in items]\n        min_datetime = convert_timestamp(\n            np.nanmin(item_list)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(item_list)).strftime(convert_format)\n        return min_datetime, max_datetime",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 892,
                                "end_lineno": 959,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/data/owselectrows.py",
                            "type": "class",
                            "name": "DateTimeWidget",
                            "content": "class DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        item_list = [item for items in list(column) for item in items]\n        min_datetime = convert_timestamp(\n            np.nanmin(item_list)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(item_list)).strftime(convert_format)\n        return min_datetime, max_datetime",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 892,
                            "end_lineno": 959,
                            "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 time\nfrom unittest.mock import Mock, patch\nimport numpy as np\n\nfrom AnyQt.QtCore import QLocale, Qt, QDate\nfrom AnyQt.QtTest import QTest\nfrom AnyQt.QtWidgets import QLineEdit, QComboBox\n\nfrom Orange.data import (\n    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,\n    Domain)\nfrom Orange.preprocess import discretize\nfrom Orange.widgets.data import owselectrows\nfrom Orange.widgets.data.owselectrows import (\n    OWSelectRows, FilterDiscreteType, SelectRowsContextHandler, DateTimeWidget)\nfrom Orange.widgets.tests.base import WidgetTest, datasets\n\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.widgets.tests.utils import simulate, override_locale\nfrom Orange.widgets.utils.annotated_data import ANNOTATED_DATA_FEATURE_NAME\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.tests import test_filename\nfrom orangewidget.settings import VERSION_KEY\n\nCFValues = {\n    FilterContinuous.Equal: [\"5.4\"],\n    FilterContinuous.NotEqual: [\"5.4\"],\n    FilterContinuous.Less: [\"5.4\"],\n    FilterContinuous.LessEqual: [\"5.4\"],\n    FilterContinuous.Greater: [\"5.4\"],\n    FilterContinuous.GreaterEqual: [\"5.4\"],\n    FilterContinuous.Between: [\"5.4\", \"6.0\"],\n    FilterContinuous.Outside: [\"5.4\", \"6.0\"],\n    FilterContinuous.IsDefined: [],\n}\n\n\nSFValues = {\n    FilterString.Equal: [\"aardwark\"],\n    FilterString.NotEqual: [\"aardwark\"],\n    FilterString.Less: [\"aardwark\"],\n    FilterString.LessEqual: [\"aardwark\"],\n    FilterString.Greater: [\"aardwark\"],\n    FilterString.GreaterEqual: [\"aardwark\"],\n    FilterString.Between: [\"aardwark\", \"cat\"],\n    FilterString.Outside: [\"aardwark\"],\n    FilterString.Contains: [\"aa\"],\n    FilterString.StartsWith: [\"aa\"],\n    FilterString.EndsWith: [\"ark\"],\n    FilterString.IsDefined: []\n}\n\nDFValues = {\n    FilterDiscreteType.Equal: [0],\n    FilterDiscreteType.NotEqual: [0],\n    FilterDiscreteType.In: [0, 1],\n    FilterDiscreteType.IsDefined: [],\n}\n\n\nclass TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))\n",
            "new_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring,unsubscriptable-object\nimport time\nfrom unittest.mock import Mock, patch\nimport numpy as np\n\nfrom AnyQt.QtCore import QLocale, Qt, QDate\nfrom AnyQt.QtTest import QTest\nfrom AnyQt.QtWidgets import QLineEdit, QComboBox\n\nfrom Orange.data import (\n    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,\n    Domain, TimeVariable)\nfrom Orange.preprocess import discretize\nfrom Orange.widgets.data import owselectrows\nfrom Orange.widgets.data.owselectrows import (\n    OWSelectRows, FilterDiscreteType, SelectRowsContextHandler, DateTimeWidget)\nfrom Orange.widgets.tests.base import WidgetTest, datasets\n\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.widgets.tests.utils import simulate, override_locale\nfrom Orange.widgets.utils.annotated_data import ANNOTATED_DATA_FEATURE_NAME\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.tests import test_filename\nfrom orangewidget.settings import VERSION_KEY\n\nCFValues = {\n    FilterContinuous.Equal: [\"5.4\"],\n    FilterContinuous.NotEqual: [\"5.4\"],\n    FilterContinuous.Less: [\"5.4\"],\n    FilterContinuous.LessEqual: [\"5.4\"],\n    FilterContinuous.Greater: [\"5.4\"],\n    FilterContinuous.GreaterEqual: [\"5.4\"],\n    FilterContinuous.Between: [\"5.4\", \"6.0\"],\n    FilterContinuous.Outside: [\"5.4\", \"6.0\"],\n    FilterContinuous.IsDefined: [],\n}\n\n\nSFValues = {\n    FilterString.Equal: [\"aardwark\"],\n    FilterString.NotEqual: [\"aardwark\"],\n    FilterString.Less: [\"aardwark\"],\n    FilterString.LessEqual: [\"aardwark\"],\n    FilterString.Greater: [\"aardwark\"],\n    FilterString.GreaterEqual: [\"aardwark\"],\n    FilterString.Between: [\"aardwark\", \"cat\"],\n    FilterString.Outside: [\"aardwark\"],\n    FilterString.Contains: [\"aa\"],\n    FilterString.StartsWith: [\"aa\"],\n    FilterString.EndsWith: [\"ark\"],\n    FilterString.IsDefined: []\n}\n\nDFValues = {\n    FilterDiscreteType.Equal: [0],\n    FilterDiscreteType.NotEqual: [0],\n    FilterDiscreteType.In: [0, 1],\n    FilterDiscreteType.IsDefined: [],\n}\n\nTFValues = {\n    FilterContinuous.Equal: [QDate(2013, 5, 5)],\n    FilterContinuous.NotEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Less: [QDate(2013, 5, 5)],\n    FilterContinuous.LessEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Greater: [QDate(2013, 5, 5)],\n    FilterContinuous.GreaterEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Between: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.Outside: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.IsDefined: [],\n}\n\n\nclass TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/data/tests/test_owselectrows.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "adf1187ab",
                "new_commit_hash": "17f3cc6ec",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/data/tests/test_owselectrows.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/data/tests/test_owselectrows.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 10,
                            "length": 7
                        },
                        "new_range": {
                            "start": 10,
                            "length": 7
                        },
                        "section": "from AnyQt.QtWidgets import QLineEdit, QComboBox"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.data import (",
                                "type": "context"
                            },
                            {
                                "content": "    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,",
                                "type": "context"
                            },
                            {
                                "content": "    Domain)",
                                "type": "deleted"
                            },
                            {
                                "content": "    Domain, TimeVariable)",
                                "type": "added"
                            },
                            {
                                "content": "from Orange.preprocess import discretize",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.data import owselectrows",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.data.owselectrows import (",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "statement",
                            "name": "",
                            "content": "from Orange.data import (\n    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,\n    Domain, TimeVariable)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 11,
                            "end_lineno": 13,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "statement",
                            "name": "",
                            "content": "from Orange.data import (\n    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,\n    Domain)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 11,
                            "end_lineno": 13,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 59,
                            "length": 6
                        },
                        "new_range": {
                            "start": 59,
                            "length": 18
                        },
                        "section": "DFValues = {"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    FilterDiscreteType.IsDefined: [],",
                                "type": "context"
                            },
                            {
                                "content": "}",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "TFValues = {",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.Equal: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.NotEqual: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.Less: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.LessEqual: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.Greater: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.GreaterEqual: [QDate(2013, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.Between: [QDate(2013, 5, 5), QDate(2015, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.Outside: [QDate(2013, 5, 5), QDate(2015, 5, 5)],",
                                "type": "added"
                            },
                            {
                                "content": "    FilterContinuous.IsDefined: [],",
                                "type": "added"
                            },
                            {
                                "content": "}",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class TestOWSelectRows(WidgetTest):",
                                "type": "context"
                            },
                            {
                                "content": "    def setUp(self):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "statement",
                            "name": "",
                            "content": "TFValues = {\n    FilterContinuous.Equal: [QDate(2013, 5, 5)],\n    FilterContinuous.NotEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Less: [QDate(2013, 5, 5)],\n    FilterContinuous.LessEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Greater: [QDate(2013, 5, 5)],\n    FilterContinuous.GreaterEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Between: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.Outside: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.IsDefined: [],\n}",
                            "ast_type_str": "Assign",
                            "start_lineno": 62,
                            "end_lineno": 72,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 75,
                            "length": 6
                        },
                        "new_range": {
                            "start": 87,
                            "length": 17
                        },
                        "section": "class TestOWSelectRows(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "context"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        # continuous var in metas",
                                "type": "added"
                            },
                            {
                                "content": "        iris = Table.from_table(",
                                "type": "added"
                            },
                            {
                                "content": "            Domain([], metas=[iris.domain.attributes[0]]), iris",
                                "type": "added"
                            },
                            {
                                "content": "        )",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.set_data(iris)",
                                "type": "added"
                            },
                            {
                                "content": "        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.remove_all()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_filter_str(self):",
                                "type": "context"
                            },
                            {
                                "content": "        zoo = Table(\"zoo\")[::5]",
                                "type": "context"
                            },
                            {
                                "content": "        self.widget.auto_commit = False",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "method",
                            "name": "TestOWSelectRows.test_filter_cont",
                            "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 79,
                            "end_lineno": 99,
                            "parent": {
                                "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                                "type": "class",
                                "name": "TestOWSelectRows",
                                "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 75,
                                "end_lineno": 695,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "class",
                            "name": "TestOWSelectRows",
                            "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 75,
                            "end_lineno": 695,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 96,
                            "length": 6
                        },
                        "new_range": {
                            "start": 119,
                            "length": 39
                        },
                        "section": "class TestOWSelectRows(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "context"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        # discrete var in metas",
                                "type": "added"
                            },
                            {
                                "content": "        lenses = Table.from_table(",
                                "type": "added"
                            },
                            {
                                "content": "            Domain([], metas=[lenses.domain.attributes[0]]), lenses",
                                "type": "added"
                            },
                            {
                                "content": "        )",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.set_data(lenses)",
                                "type": "added"
                            },
                            {
                                "content": "        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.remove_all()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_filter_time(self):",
                                "type": "added"
                            },
                            {
                                "content": "        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.auto_commit = False",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.set_data(data)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.remove_all()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        # time var in metas",
                                "type": "added"
                            },
                            {
                                "content": "        data = Table.from_table(",
                                "type": "added"
                            },
                            {
                                "content": "            Domain([], metas=[data.domain[\"breach_start\"]]), data",
                                "type": "added"
                            },
                            {
                                "content": "        )",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.set_data(data)",
                                "type": "added"
                            },
                            {
                                "content": "        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.remove_all()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.add_row(data.domain.metas[0], i, TFValues[op])",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.conditions_changed()",
                                "type": "added"
                            },
                            {
                                "content": "            self.widget.unconditional_commit()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    @override_locale(QLocale.C)  # Locale with decimal point",
                                "type": "context"
                            },
                            {
                                "content": "    def test_continuous_filter_with_c_locale(self):",
                                "type": "context"
                            },
                            {
                                "content": "        iris = Table(\"iris\")[:5]",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "class",
                            "name": "TestOWSelectRows",
                            "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 75,
                            "end_lineno": 695,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "method",
                            "name": "TestOWSelectRows.test_filter_disc",
                            "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 111,
                            "end_lineno": 131,
                            "parent": {
                                "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                                "type": "class",
                                "name": "TestOWSelectRows",
                                "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 75,
                                "end_lineno": 695,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                            "type": "method",
                            "name": "TestOWSelectRows.test_filter_time",
                            "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 133,
                            "end_lineno": 153,
                            "parent": {
                                "file_name": "Orange/widgets/data/tests/test_owselectrows.py",
                                "type": "class",
                                "name": "TestOWSelectRows",
                                "content": "class TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 75,
                                "end_lineno": 695,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "945e235a69847d01e4c8f2800da36f29ec0a4ea4^",
    "new_commit_hash": "945e235a69847d01e4c8f2800da36f29ec0a4ea4",
    "commit_message": "Merge pull request #4912 from PrimozGodec/fix-timevarmetacharsh\n\n[FIX] Select Rows: fix fail when time variable in metas",
    "commit_date": "2020-07-24T11:56:35+02:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "945e235a69847d01e4c8f2800da36f29ec0a4ea4",
    "test_file_codes": [
        "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring,unsubscriptable-object\nimport time\nfrom unittest.mock import Mock, patch\nimport numpy as np\n\nfrom AnyQt.QtCore import QLocale, Qt, QDate\nfrom AnyQt.QtTest import QTest\nfrom AnyQt.QtWidgets import QLineEdit, QComboBox\n\nfrom Orange.data import (\n    Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,\n    Domain, TimeVariable)\nfrom Orange.preprocess import discretize\nfrom Orange.widgets.data import owselectrows\nfrom Orange.widgets.data.owselectrows import (\n    OWSelectRows, FilterDiscreteType, SelectRowsContextHandler, DateTimeWidget)\nfrom Orange.widgets.tests.base import WidgetTest, datasets\n\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.widgets.tests.utils import simulate, override_locale\nfrom Orange.widgets.utils.annotated_data import ANNOTATED_DATA_FEATURE_NAME\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.tests import test_filename\nfrom orangewidget.settings import VERSION_KEY\n\nCFValues = {\n    FilterContinuous.Equal: [\"5.4\"],\n    FilterContinuous.NotEqual: [\"5.4\"],\n    FilterContinuous.Less: [\"5.4\"],\n    FilterContinuous.LessEqual: [\"5.4\"],\n    FilterContinuous.Greater: [\"5.4\"],\n    FilterContinuous.GreaterEqual: [\"5.4\"],\n    FilterContinuous.Between: [\"5.4\", \"6.0\"],\n    FilterContinuous.Outside: [\"5.4\", \"6.0\"],\n    FilterContinuous.IsDefined: [],\n}\n\n\nSFValues = {\n    FilterString.Equal: [\"aardwark\"],\n    FilterString.NotEqual: [\"aardwark\"],\n    FilterString.Less: [\"aardwark\"],\n    FilterString.LessEqual: [\"aardwark\"],\n    FilterString.Greater: [\"aardwark\"],\n    FilterString.GreaterEqual: [\"aardwark\"],\n    FilterString.Between: [\"aardwark\", \"cat\"],\n    FilterString.Outside: [\"aardwark\"],\n    FilterString.Contains: [\"aa\"],\n    FilterString.StartsWith: [\"aa\"],\n    FilterString.EndsWith: [\"ark\"],\n    FilterString.IsDefined: []\n}\n\nDFValues = {\n    FilterDiscreteType.Equal: [0],\n    FilterDiscreteType.NotEqual: [0],\n    FilterDiscreteType.In: [0, 1],\n    FilterDiscreteType.IsDefined: [],\n}\n\nTFValues = {\n    FilterContinuous.Equal: [QDate(2013, 5, 5)],\n    FilterContinuous.NotEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Less: [QDate(2013, 5, 5)],\n    FilterContinuous.LessEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Greater: [QDate(2013, 5, 5)],\n    FilterContinuous.GreaterEqual: [QDate(2013, 5, 5)],\n    FilterContinuous.Between: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.Outside: [QDate(2013, 5, 5), QDate(2015, 5, 5)],\n    FilterContinuous.IsDefined: [],\n}\n\n\nclass TestOWSelectRows(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWSelectRows)  # type: OWSelectRows\n\n    def test_filter_cont(self):\n        iris = Table(\"iris\")[::5]\n        self.widget.auto_commit = True\n        self.widget.set_data(iris)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # continuous var in metas\n        iris = Table.from_table(\n            Domain([], metas=[iris.domain.attributes[0]]), iris\n        )\n        self.widget.set_data(iris)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[ContinuousVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(iris.domain.metas[0], i, CFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_str(self):\n        zoo = Table(\"zoo\")[::5]\n        self.widget.auto_commit = False\n        self.widget.set_data(zoo)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[StringVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(zoo.domain.metas[0], i, SFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_disc(self):\n        lenses = Table(test_filename(\"datasets/lenses.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(lenses)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(0, i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # discrete var in metas\n        lenses = Table.from_table(\n            Domain([], metas=[lenses.domain.attributes[0]]), lenses\n        )\n        self.widget.set_data(lenses)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[DiscreteVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(lenses.domain.metas[0], i, DFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_continuous_filter_with_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # Validating with C locale should accept decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5.2\",))\n\n        # but not decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_continuous_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        # but not decimal point\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5.2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"52\",))\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_all_numeric_filter_with_c_locale_from_context(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3.14\"))\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_all_numeric_filter_with_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        widget = self.widget_with_context(\n            iris.domain, [[\"All numeric variables\", None, 0, (3.14, )]])\n        self.send_signal(widget.Inputs.data, iris)\n        self.assertTrue(widget.conditions[0][2][0].startswith(\"3,14\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_stores_settings_in_invariant_locale(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        # sl_SI locale should accept decimal comma\n        self.widget.remove_all_button.click()\n        self.enterFilter(iris.domain[2], \"is below\", \"5,2\")\n        self.assertEqual(self.widget.conditions[0][2], (\"5,2\",))\n\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        saved_condition = context.values[\"conditions\"][0]\n        self.assertEqual(saved_condition[3][0], 5.2)\n\n    @override_locale(QLocale.C)  # Locale with decimal point\n    def test_store_all_numeric_filter_with_c_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3.14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.Slovenian)  # Locale with decimal comma\n    def test_store_all_numeric_filter_with_sl_SI_locale_to_context(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"3,14\")\n        context = self.widget.current_context\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertEqual(context.values[\"conditions\"][0][3], [3.14])\n\n    @override_locale(QLocale.C)\n    def test_restores_continuous_filter_in_c_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5.2\"))\n\n    @override_locale(QLocale.Slovenian)\n    def test_restores_continuous_filter_in_sl_SI_locale(self):\n        iris = Table(\"iris\")[:5]\n        # Settings with string value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n        # Settings with float value\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 102, 2, (5.2,)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        values = self.widget.conditions[0][2]\n        self.assertTrue(values[0].startswith(\"5,2\"))\n\n    @override_locale(QLocale.C)\n    def test_partial_matches(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[:2], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[0])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_partial_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2,\n                      (class_var.values[0], class_var.values[2])]])\n\n        # sanity checks\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 3))  # indices of values + 1\n\n        # actual test\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (2, ))  # index of value + 1\n\n    def test_backward_compat_match_values(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        class_var = domain.class_var\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 2)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (1, 2))  # index of value + 1\n\n        # reset to [0] if out of range\n        self.widget = self.widget_with_context(\n            domain, [[class_var.name, 1, 2, (1, 3)]])\n\n        new_class_var = DiscreteVariable(class_var.name, class_var.values[1:])\n        new_domain = Domain(domain.attributes, new_class_var)\n        non0 = iris.Y != 0\n        iris2 = Table.from_numpy(new_domain, iris.X[non0], iris.Y[non0] - 1)\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertIs(condition[0], new_class_var)\n        self.assertEqual(condition[1], 2)\n        self.assertEqual(condition[2], (0, ))  # index of value + 1\n\n    @override_locale(QLocale.C)\n    def test_partial_matches_with_missing_vars(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.widget = self.widget_with_context(\n            domain, [[domain[0].name, 2, 2, (\"5.2\",)],\n                     [domain[2].name, 2, 2, (\"4.2\",)]])\n        iris2 = iris.transform(Domain(domain.attributes[2:], None))\n        self.send_signal(self.widget.Inputs.data, iris2)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], domain[2])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"4.2\"))\n\n    def test_load_settings(self):\n        iris = Table(\"iris\")[:5]\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        sepal_length, sepal_width = iris.domain[:2]\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(sepal_width, \"is below\", \"5.2\")\n        self.enterFilter(sepal_length, \"is at most\", \"4\")\n        data = self.widget.settingsHandler.pack_data(self.widget)\n\n        w2 = self.create_widget(OWSelectRows, data)\n        self.send_signal(self.widget.Inputs.data, iris, widget=w2)\n\n        var_combo = w2.cond_list.cellWidget(0, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal width\")\n        oper_combo = w2.cond_list.cellWidget(0, 1)\n        self.assertEqual(oper_combo.currentText(), \"is below\")\n\n        var_combo = w2.cond_list.cellWidget(1, 0)\n        self.assertEqual(var_combo.currentText(), \"sepal length\")\n        oper_combo = w2.cond_list.cellWidget(1, 1)\n        self.assertEqual(oper_combo.currentText(), \"is at most\")\n\n    def test_is_defined_on_continuous_variable(self):\n        # gh-2054 regression\n\n        data = Table(datasets.path(\"testing_dataset_cls\"))\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[\"c2\"], \"is defined\")\n        self.assertFalse(self.widget.Error.parsing_error.is_shown())\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 3)\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), 1)\n        self.assertEqual(len(self.get_output(\"Data\")), len(data))\n\n        # Test saving of settings\n        self.widget.settingsHandler.pack_data(self.widget)\n\n    def test_summary(self):\n        \"\"\"Check if status bar displays correct input/output summary\"\"\"\n        input_sum = self.widget.info.set_input_summary = Mock()\n        output_sum = self.widget.info.set_output_summary = Mock()\n\n        data = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, data)\n        input_sum.assert_called_with(len(data), format_summary_details(data))\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n\n        self.enterFilter(data.domain[\"iris\"], \"is\", \"Iris-setosa\")\n        output = self.get_output(\"Matching Data\")\n        output_sum.assert_called_with(len(output),\n                                      format_summary_details(output))\n        input_sum.reset_mock()\n        output_sum.reset_mock()\n        self.send_signal(self.widget.Inputs.data, None)\n        input_sum.assert_called_once()\n        self.assertEqual(input_sum.call_args[0][0].brief, \"\")\n        output_sum.assert_called_once()\n        self.assertEqual(output_sum.call_args[0][0].brief, \"\")\n\n    def test_output_filter(self):\n        \"\"\"\n        None on output when there is no data.\n        GH-2726\n        \"\"\"\n        data = Table(\"iris\")[:10]\n        len_data = len(data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.enterFilter(data.domain[0], \"is below\", \"-1\")\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.assertEqual(len(self.get_output(\"Unmatched Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n        self.widget.remove_all_button.click()\n        self.enterFilter(data.domain[0], \"is below\", \"10\")\n        self.assertIsNone(self.get_output(\"Unmatched Data\"))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), len_data)\n        self.assertEqual(len(self.get_output(\"Data\")), len_data)\n\n    def test_annotated_data(self):\n        iris = Table(\"iris\")\n        self.send_signal(self.widget.Inputs.data, iris)\n\n        self.enterFilter(iris.domain[\"iris\"], \"is\", \"Iris-setosa\")\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 150)\n        annotations = annotated.get_column_view(ANNOTATED_DATA_FEATURE_NAME)[0]\n        np.testing.assert_equal(annotations[:50], True)\n        np.testing.assert_equal(annotations[50:], False)\n\n    def test_change_var_type(self):\n        iris = Table(\"iris\")\n        domain = iris.domain\n\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[0], \"is below\", \"5.2\")\n\n        var0vals = list({str(x) for x in iris.X[:, 0]})\n        new_domain = Domain(\n            (DiscreteVariable(domain[0].name, values=var0vals), )\n            + domain.attributes[1:],\n            domain.class_var)\n        new_iris = iris.transform(new_domain)\n        self.send_signal(self.widget.Inputs.data, new_iris)\n\n    def test_keep_operator(self):\n        data = Table(\"heart_disease\")\n        domain = data.domain\n\n        self.send_signal(self.widget.Inputs.data, data)\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is not\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is not\")\n\n        self.widget.remove_all_button.click()\n        self.enterFilter(domain[\"age\"], \"is below\", \"42\")\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"chest pain\", delay=0)\n        self.assertEqual(\n            self.widget.cond_list.cellWidget(0, 1).currentText(), \"is\")\n\n    def test_calendar_dates(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.send_signal(self.widget.Inputs.data, data)\n        simulate.combobox_activate_item(\n            self.widget.cond_list.cellWidget(0, 0), \"Date_Posted_or_Updated\",\n            delay=0)\n        value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]\n        self.assertIsInstance(value_combo, DateTimeWidget)\n\n        # first displayed date is min date\n        self.assertEqual(value_combo.date(), QDate(2014, 1, 23))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 691)\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is below\",\n                         QDate(2014, 4, 17))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 840)\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is greater than\",\n                         QDate(2014, 6, 30))\n        self.assertIsNone(self.get_output(\"Matching Data\"))\n        self.widget.remove_all_button.click()\n        # date is in range min-max date\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2013, 1, 1))\n        self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))\n        self.enterFilter(\"Date_Posted_or_Updated\", \"equals\", QDate(2015, 1, 1))\n        self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))\n        self.widget.remove_all_button.click()\n        # no date crossings\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 1, 23))\n        self.assertEqual(self.widget.conditions[0][2],\n                         (QDate(2014, 4, 17), QDate(2014, 4, 17)))\n        self.widget.remove_all_button.click()\n        self.enterFilter(\"Date_Posted_or_Updated\", \"is between\",\n                         QDate(2014, 4, 17), QDate(2014, 4, 30))\n        self.assertEqual(len(self.get_output(\"Matching Data\")), 58)\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_add_all(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars + domain.attributes))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Cancel)\n    def test_add_all_cancel(self, msgbox):\n        iris = Table(\"iris\")\n        domain = iris.domain\n        self.send_signal(self.widget.Inputs.data, iris)\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n        self.widget.add_all_button.click()\n        msgbox.assert_called()\n        self.assertEqual([cond[0] for cond in self.widget.conditions],\n                         list(domain.class_vars))\n\n    @patch.object(owselectrows.QMessageBox, \"question\",\n                  return_value=owselectrows.QMessageBox.Ok)\n    def test_report(self, _):\n        zoo = Table(\"zoo\")\n        self.send_signal(self.widget.Inputs.data, zoo)\n        self.widget.add_all_button.click()\n        self.enterFilter(\"All numeric variables\", \"equal\", \"42\")\n        self.enterFilter(zoo.domain[0], \"is defined\")\n        self.enterFilter(zoo.domain[1], \"is one of\")\n        self.widget.send_report()  # don't crash\n\n    # Uncomment this on 2022/2/2\n    #\n    # def test_migration_to_version_1(self):\n    #     iris = Table(\"iris\")\n    #\n    #     ch = SelectRowsContextHandler()\n    #     context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))\n    #     context.values = dict(conditions=[[\"petal length\", 2, (5.2,)]])\n    #     settings = dict(context_settings=[context])\n    #     widget = self.create_widget(OWSelectRows, settings)\n    #     self.assertEqual(widget.conditions, [])\n\n    @override_locale(QLocale.C)\n    def test_support_old_settings(self):\n        iris = Table(\"iris\")\n        self.widget = self.widget_with_context(\n            iris.domain, [[\"sepal length\", 2, (\"5.2\",)]])\n        self.send_signal(self.widget.Inputs.data, iris)\n        condition = self.widget.conditions[0]\n        self.assertEqual(condition[0], iris.domain[\"sepal length\"])\n        self.assertEqual(condition[1], 2)\n        self.assertTrue(condition[2][0].startswith(\"5.2\"))\n\n    def test_end_support_for_version_1(self):\n        if time.gmtime() >= (2022, 2, 2):\n            self.fail(\"\"\"\nHappy 22/2/2!\n\nNow remove support for version==None settings in\nSelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nand uncomment OWSelectRows.migrate.\n\nIn tests, uncomment test_migration_to_version_1,\nand remove test_support_old_settings and this test.\n\nBasically, revert this commit.\n\"\"\")\n\n    def test_purge_discretized(self):\n        housing = Table(\"housing\")\n        method = discretize.EqualFreq(n=3)\n        discretizer = discretize.DomainDiscretizer(\n            discretize_class=True, method=method)\n        domain = discretizer(housing)\n        data = housing.transform(domain)\n        widget = self.widget_with_context(domain, [[\"MEDV\", 101, 2, (2, 3)]])\n        widget.purge_classes = True\n        self.send_signal(widget.Inputs.data, data)\n        out = self.get_output(widget.Outputs.matching_data)\n        expected = data.Y[(data.Y == 1) + (data.Y == 2)]\n        expected = (expected == 2).astype(float)\n        np.testing.assert_equal(out.Y, expected)\n\n    def test_meta_setting(self):\n        \"\"\"\n        Test if all conditions from all segments (attributes, class, meta)\n        stores correctly\n        \"\"\"\n        data = Table(\"iris\")\n        data = Table.from_table(\n            Domain(\n                data.domain.attributes[:3],\n                data.domain.class_var,\n                data.domain.attributes[3:]\n            ), data)\n        self.send_signal(self.widget.Inputs.data, data)\n\n        vars_ = [\n            data.domain.metas[0],\n            data.domain.attributes[0],\n            data.domain.class_var\n        ]\n        cond = [0, 0, 0]\n        val = [(0, ), (0, ), (1, )]\n        conds = list(zip(vars_, cond, val))\n\n        self.widget.conditions = conds\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n        # when sending new-same data conditions are restored from the context\n        self.send_signal(self.widget.Inputs.data, data)\n        self.assertListEqual([c[0] for c in self.widget.conditions], vars_)\n\n    def widget_with_context(self, domain, conditions):\n        ch = SelectRowsContextHandler()\n        context = ch.new_context(domain, *ch.encode_domain(domain))\n        context.values = {\"conditions\": conditions,\n                          VERSION_KEY: OWSelectRows.settings_version}\n        settings = dict(context_settings=[context])\n\n        return self.create_widget(OWSelectRows, settings)\n\n    def enterFilter(self, variable, filter, value1=None, value2=None):\n        # pylint: disable=redefined-builtin\n        row = self.widget.cond_list.model().rowCount()\n        self.widget.add_button.click()\n\n        var_combo = self.widget.cond_list.cellWidget(row, 0)\n        name = variable.name if isinstance(variable, Variable) else variable\n        simulate.combobox_activate_item(var_combo, name, delay=0)\n\n        oper_combo = self.widget.cond_list.cellWidget(row, 1)\n        simulate.combobox_activate_item(oper_combo, filter, delay=0)\n\n        value_inputs = self.__get_value_widgets(row)\n        for i, value in enumerate([value1, value2]):\n            if value is None:\n                continue\n            self.__set_value(value_inputs[i], value)\n\n    def __get_value_widgets(self, row):\n        value_inputs = self.widget.cond_list.cellWidget(row, 2)\n        if value_inputs:\n            if isinstance(value_inputs, QComboBox):\n                value_inputs = [value_inputs]\n            else:\n                value_input = []\n                for widget in value_inputs.children():\n                    if isinstance(widget, QLineEdit):\n                        value_input.append(widget)\n                    elif isinstance(widget, DateTimeWidget):\n                        value_input.append(widget)\n                return value_input\n        return value_inputs\n\n    @staticmethod\n    def __set_value(widget, value):\n        if isinstance(widget, QLineEdit):\n            QTest.mouseClick(widget, Qt.LeftButton)\n            QTest.keyClicks(widget, value, delay=0)\n            QTest.keyClick(widget, Qt.Key_Enter)\n        elif isinstance(widget, QComboBox):\n            simulate.combobox_activate_item(widget, value)\n        elif isinstance(widget, DateTimeWidget):\n            widget.setDate(value)\n        else:\n            raise ValueError(\"Unsupported widget {}\".format(widget))\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\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpv8uqvdyr/tmp\ncreating /tmp/tmpv8uqvdyr/tmp/tmpv8uqvdyr\ncompile options: '-MMD -MF /tmp/tmpv8uqvdyr/file.c.d -c'\ncc: /tmp/tmpv8uqvdyr/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpaexntix2/tmp\ncreating /tmp/tmpaexntix2/tmp/tmpaexntix2\ncompile options: '-MMD -MF /tmp/tmpaexntix2/file.c.d -c'\ncc: /tmp/tmpaexntix2/file.c\ncustomize UnixCCompiler using build_ext\nbuilding 'Orange.classification._simple_tree' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_simple_tree.c\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 Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._tree_scorers' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_tree_scorers.c\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 Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_valuecount.c\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 Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_contingency.c\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 Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_io.c\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 Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_variable.c\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 Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/distance/_distance.c\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 Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/preprocess/_discretize.c\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 Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/preprocess/_relieff.cpp\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 Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/projection/_som.c\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 Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/widgets/utils/_grid_density.cpp\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 Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\nrunning develop\nrunning build_scripts\nrunning egg_info\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\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'\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'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpup5leqiz/tmp\ncreating /tmp/tmpup5leqiz/tmp/tmpup5leqiz\ncompile options: '-MMD -MF /tmp/tmpup5leqiz/file.c.d -c'\ncc: /tmp/tmpup5leqiz/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpzbp6q6x0/tmp\ncreating /tmp/tmpzbp6q6x0/tmp/tmpzbp6q6x0\ncompile options: '-MMD -MF /tmp/tmpzbp6q6x0/file.c.d -c'\ncc: /tmp/tmpzbp6q6x0/file.c\ncustomize UnixCCompiler using build_ext\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.27.0.dev0+945e235 to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4\nProcessing dependencies for Orange3==3.27.0.dev0+945e235\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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages\nSearching for httpx==0.24.1\nBest match: httpx 0.24.1\nAdding httpx 0.24.1 to easy-install.pth file\nInstalling httpx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pyrcc5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pyuic5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages\nSearching for httpcore==0.17.3\nBest match: httpcore 0.17.3\nAdding httpcore 0.17.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages\nSearching for anyio==3.7.1\nBest match: anyio 3.7.1\nAdding anyio 3.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.27.0.dev0+945e235\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 47ms\nPrepared 18 packages in 3ms\nInstalled 18 packages in 148ms\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 54 packages in 402ms\nInstalled 47 packages in 824ms\n + anyio==3.7.1\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.17.3\n + httpx==0.24.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 + 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 145ms\nInstalled 13 packages in 43ms\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 4ms\nInstalled 2 packages in 32ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nAudited 1 package in 1ms\nAudited 2 packages in 1ms\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      |         ^~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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:3115:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3115 |   __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:4619:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4619 |   __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:5396:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5396 |           for (__pyx_t_22 = 0; __pyx_t_22 < __pyx_t_21; __pyx_t_22+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5517:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5517 |           __pyx_t_27 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5523:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5523 |           __pyx_t_27 = (((__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:7051:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7051 |         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:7741:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7741 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7801:143: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7801 |               __pyx_t_17 = (((*((__pyx_t_5numpy_int32_t *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_18 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                               ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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:3881:24: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3881 |           __pyx_v_decs = (__pyx_v_decs / 10.0);\n      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:4070:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4070 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 221, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:480:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  480 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1301:7: note: ‘__pyx_v_attr_index’ was declared here\n 1301 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:2935:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2935 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:627:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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:3655:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3655 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3654:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3654 |   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:4547:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4547 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4114:7: note: ‘__pyx_v_win_y’ was declared here\n 4114 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4536:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4536 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_36, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_37, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4113:7: note: ‘__pyx_v_win_x’ was declared here\n 4113 |   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:4746:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4746 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4745:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4745 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3141:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3141 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:2964:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2964 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2665:7: note: ‘__pyx_v_win_y’ was declared here\n 2665 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:2953:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2953 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2664:7: note: ‘__pyx_v_win_x’ was declared here\n 2664 |   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:5515:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5515 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5514:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5514 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.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,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/.venv/lib/python3.7/site-packages/setuptools/command/egg_info.py:628: SetuptoolsDeprecationWarning: Custom 'build_py' does not implement 'get_data_files_without_manifest'.\nPlease extend command classes from setuptools instead of distutils.\n  SetuptoolsDeprecationWarning\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_945e235a69847d01e4c8f2800da36f29ec0a4ea4\nplugins: anyio-3.7.1\ncollected 39 items\n\nr2e_tests/test_1.py sssE..........F.........F..............              [100%]\n\n==================================== ERRORS ====================================\n_______________________ ERROR at setup of test_filename ________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/Orange/tests/__init__.py, line 65\n  def test_filename(path):\nE       fixture 'path' not found\n>       available fixtures: anyio_backend, anyio_backend_name, anyio_backend_options, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/Orange/tests/__init__.py:65\n=================================== FAILURES ===================================\n_______________ TestOWSelectRows.test_end_support_for_version_1 ________________\n\nself = <r2e_tests.test_1.TestOWSelectRows testMethod=test_end_support_for_version_1>\n\n        def test_end_support_for_version_1(self):\n            if time.gmtime() >= (2022, 2, 2):\n                self.fail(\"\"\"\n    Happy 22/2/2!\n    \n    Now remove support for version==None settings in\n    SelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\n    and uncomment OWSelectRows.migrate.\n    \n    In tests, uncomment test_migration_to_version_1,\n    and remove test_support_old_settings and this test.\n    \n    Basically, revert this commit.\n>   \"\"\")\nE   AssertionError: \nE   Happy 22/2/2!\nE   \nE   Now remove support for version==None settings in\nE   SelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nE   and uncomment OWSelectRows.migrate.\nE   \nE   In tests, uncomment test_migration_to_version_1,\nE   and remove test_support_old_settings and this test.\nE   \nE   Basically, revert this commit.\n\nr2e_tests/test_1.py:595: AssertionError\n______________________ TestOWSelectRows.test_minimum_size ______________________\n\nself = <r2e_tests.test_1.TestOWSelectRows testMethod=test_minimum_size>\n\n    def test_minimum_size(self):\n        widget = getattr(self, \"widget\", None)\n        if widget is None:\n            self.skipTest(\"minimum size not tested as .widget was not set\")\n>       self.check_minimum_size(widget)\n\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:242: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:615: in check_minimum_size\n    self.assertLess(min_size.width(), 800)\nE   AssertionError: 815 not less than 800\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_add_all\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_add_all_cancel\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_all_numeric_filter_with_c_locale_from_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_all_numeric_filter_with_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_annotated_data\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_backward_compat_match_values\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_calendar_dates\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_change_var_type\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_continuous_filter_with_c_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_continuous_filter_with_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_cont\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_disc\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_str\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_time\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_image_export\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_is_defined_on_continuous_variable\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_keep_operator\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_load_settings\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_meta_setting\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_output_filter\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_match_values\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_matches\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_matches_with_missing_vars\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_purge_discretized\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_report\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_restores_continuous_filter_in_c_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_restores_continuous_filter_in_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_store_all_numeric_filter_with_c_locale_to_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_store_all_numeric_filter_with_sl_SI_locale_to_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_stores_settings_in_invariant_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_summary\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_support_old_settings\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\nERROR r2e_tests/test_1.py::test_filename\nFAILED r2e_tests/test_1.py::TestOWSelectRows::test_end_support_for_version_1\nFAILED r2e_tests/test_1.py::TestOWSelectRows::test_minimum_size - AssertionEr...\n=============== 2 failed, 33 passed, 3 skipped, 1 error in 7.68s ===============\nWidgets left: 0    Max widgets: 4256 \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_945e235a69847d01e4c8f2800da36f29ec0a4ea4\nplugins: anyio-3.7.1\ncollected 39 items\n\nr2e_tests/test_1.py sssE..........F...F.....F..............              [100%]\n\n==================================== ERRORS ====================================\n_______________________ ERROR at setup of test_filename ________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/Orange/tests/__init__.py, line 65\n  def test_filename(path):\nE       fixture 'path' not found\n>       available fixtures: anyio_backend, anyio_backend_name, anyio_backend_options, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_945e235a69847d01e4c8f2800da36f29ec0a4ea4/Orange/tests/__init__.py:65\n=================================== FAILURES ===================================\n_______________ TestOWSelectRows.test_end_support_for_version_1 ________________\n\nself = <r2e_tests.test_1.TestOWSelectRows testMethod=test_end_support_for_version_1>\n\n        def test_end_support_for_version_1(self):\n            if time.gmtime() >= (2022, 2, 2):\n                self.fail(\"\"\"\n    Happy 22/2/2!\n    \n    Now remove support for version==None settings in\n    SelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\n    and uncomment OWSelectRows.migrate.\n    \n    In tests, uncomment test_migration_to_version_1,\n    and remove test_support_old_settings and this test.\n    \n    Basically, revert this commit.\n>   \"\"\")\nE   AssertionError: \nE   Happy 22/2/2!\nE   \nE   Now remove support for version==None settings in\nE   SelectRowsContextHandler.decode_setting and SelectRowsContextHandler.match,\nE   and uncomment OWSelectRows.migrate.\nE   \nE   In tests, uncomment test_migration_to_version_1,\nE   and remove test_support_old_settings and this test.\nE   \nE   Basically, revert this commit.\n\nr2e_tests/test_1.py:595: AssertionError\n______________________ TestOWSelectRows.test_filter_time _______________________\n\nself = <r2e_tests.test_1.TestOWSelectRows testMethod=test_filter_time>\n\n    def test_filter_time(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        self.widget.auto_commit = False\n        self.widget.set_data(data)\n    \n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n            self.widget.add_row(data.domain[\"breach_start\"], i, TFValues[op])\n            self.widget.conditions_changed()\n            self.widget.unconditional_commit()\n    \n        # time var in metas\n        data = Table.from_table(\n            Domain([], metas=[data.domain[\"breach_start\"]]), data\n        )\n        self.widget.set_data(data)\n        for i, (op, _) in enumerate(OWSelectRows.Operators[TimeVariable]):\n            self.widget.remove_all()\n>           self.widget.add_row(data.domain.metas[0], i, TFValues[op])\n\nr2e_tests/test_1.py:151: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nOrange/widgets/data/owselectrows.py:347: in add_row\n    condition_type, condition_value)\nOrange/widgets/data/owselectrows.py:409: in set_new_operators\n    self.set_new_values(oper_combo, adding_all, selected_values)\nOrange/widgets/data/owselectrows.py:558: in set_new_values\n    w = DateTimeWidget(self, column, datetime_format)\nOrange/widgets/data/owselectrows.py:898: in __init__\n    self.set_format(column)\nOrange/widgets/data/owselectrows.py:920: in set_format\n    min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\nOrange/widgets/data/owselectrows.py:956: in find_range\n    np.nanmin(item_list)).strftime(convert_format)\n<__array_function__ internals>:6: in nanmin\n    ???\n.venv/lib/python3.7/site-packages/numpy/lib/nanfunctions.py:327: in nanmin\n    res = np.amin(a, axis=axis, out=out, **kwargs)\n<__array_function__ internals>:6: in amin\n    ???\n.venv/lib/python3.7/site-packages/numpy/core/fromnumeric.py:2746: in amin\n    keepdims=keepdims, initial=initial, where=where)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nobj = array([], dtype=float64), ufunc = <ufunc 'minimum'>, method = 'min'\naxis = None, dtype = None, out = None\nkwargs = {'initial': <no value>, 'keepdims': <no value>, 'where': <no value>}\npasskwargs = {}\n\n    def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs):\n        passkwargs = {k: v for k, v in kwargs.items()\n                      if v is not np._NoValue}\n    \n        if type(obj) is not mu.ndarray:\n            try:\n                reduction = getattr(obj, method)\n            except AttributeError:\n                pass\n            else:\n                # This branch is needed for reductions like any which don't\n                # support a dtype.\n                if dtype is not None:\n                    return reduction(axis=axis, dtype=dtype, out=out, **passkwargs)\n                else:\n                    return reduction(axis=axis, out=out, **passkwargs)\n    \n>       return ufunc.reduce(obj, axis, dtype, out, **passkwargs)\nE       ValueError: zero-size array to reduction operation minimum which has no identity\n\n.venv/lib/python3.7/site-packages/numpy/core/fromnumeric.py:90: ValueError\n______________________ TestOWSelectRows.test_minimum_size ______________________\n\nself = <r2e_tests.test_1.TestOWSelectRows testMethod=test_minimum_size>\n\n    def test_minimum_size(self):\n        widget = getattr(self, \"widget\", None)\n        if widget is None:\n            self.skipTest(\"minimum size not tested as .widget was not set\")\n>       self.check_minimum_size(widget)\n\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:242: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:615: in check_minimum_size\n    self.assertLess(min_size.width(), 800)\nE   AssertionError: 815 not less than 800\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_add_all\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_add_all_cancel\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_all_numeric_filter_with_c_locale_from_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_all_numeric_filter_with_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_annotated_data\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_backward_compat_match_values\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_calendar_dates\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_change_var_type\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_continuous_filter_with_c_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_continuous_filter_with_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_cont\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_disc\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_filter_str\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_image_export\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_is_defined_on_continuous_variable\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_keep_operator\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_load_settings\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_meta_setting\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_output_filter\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_match_values\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_matches\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_partial_matches_with_missing_vars\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_purge_discretized\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_report\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_restores_continuous_filter_in_c_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_restores_continuous_filter_in_sl_SI_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_store_all_numeric_filter_with_c_locale_to_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_store_all_numeric_filter_with_sl_SI_locale_to_context\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_stores_settings_in_invariant_locale\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_summary\nPASSED r2e_tests/test_1.py::TestOWSelectRows::test_support_old_settings\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\nERROR r2e_tests/test_1.py::test_filename\nFAILED r2e_tests/test_1.py::TestOWSelectRows::test_end_support_for_version_1\nFAILED r2e_tests/test_1.py::TestOWSelectRows::test_filter_time - ValueError: ...\nFAILED r2e_tests/test_1.py::TestOWSelectRows::test_minimum_size - AssertionEr...\n=============== 3 failed, 32 passed, 3 skipped, 1 error in 5.50s ===============\nWidgets left: 0    Max widgets: 4258 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/data/owselectrows.py Orange/widgets/data/tests/test_owselectrows.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Selecting Rows Fails When Time Variable is in Metas

**Description:**
When using the Select Rows widget with a time variable included in the metadata, the widget fails to apply the selection criteria and raises an error.

**Example Code:**
```python
from Orange.data import Table, Domain, TimeVariable

# Create a domain with a time variable in metas
domain = Domain([], metas=[TimeVariable("breach_start")])
data = Table.from_table(domain, existing_table)

# Configure the Select Rows widget
widget.set_data(data)
widget.add_row(data.domain.metas[0], operation_index, [QDate(2013, 5, 5)])
widget.conditions_changed()
widget.unconditional_commit()
```

**Expected Behavior:**
The Select Rows widget should correctly handle time variables in the metadata, allowing users to filter rows based on the specified time criteria without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message:
```
ValueError: zero-size array to reduction operation minimum which has no identity
```
This error occurs during the processing of the time variable, preventing the widget from applying the row selection.

[/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/owselectrows.py
import enum
from collections import OrderedDict
from datetime import datetime, timezone, timedelta

import numpy as np

from AnyQt.QtWidgets import (
    QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,
    QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,
    QLabel, QHBoxLayout, QDateTimeEdit, QCalendarWidget)
from AnyQt.QtGui import (QDoubleValidator, QStandardItemModel, QStandardItem,
                         QFontMetrics, QPalette)
from AnyQt.QtCore import Qt, QPoint, QPersistentModelIndex, QLocale, \
    QDateTime, QDate, QTime

from orangewidget.utils.combobox import ComboBoxSearch
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.data import (
    ContinuousVariable, DiscreteVariable, StringVariable, TimeVariable, Table)
import Orange.data.filter as data_filter
from Orange.data.filter import FilterContinuous, FilterString
from Orange.data.sql.table import SqlTable
from Orange.preprocess import Remove
from Orange.widgets import widget, gui
from Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output
from Orange.widgets.utils import vartype
from Orange.widgets import report
from Orange.widgets.widget import Msg
from Orange.widgets.utils.annotated_data import (create_annotated_table,
                                                 ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.utils.state_summary import format_summary_details


class SelectRowsContextHandler(DomainContextHandler):
    """Context handler that filters conditions"""

    # pylint: disable=arguments-differ
    def is_valid_item(self, setting, condition, attrs, metas):
        """Return True if condition applies to a variable in given domain."""
        varname, *_ = condition
        return varname in attrs or varname in metas

    def encode_setting(self, context, setting, value):
        if setting.name != 'conditions':
            return super().encode_settings(context, setting, value)

        encoded = []
        CONTINUOUS = vartype(ContinuousVariable("x"))
        for attr, op, values in value:
            if isinstance(attr, str):
                if OWSelectRows.AllTypes.get(attr) == CONTINUOUS:
                    values = [QLocale().toDouble(v)[0] for v in values]
                # None will match the value returned by all_vars.get
                encoded.append((attr, None, op, values))
            else:
                # check for exact match, pylint: disable=unidiomatic-typecheck
                if type(attr) is ContinuousVariable \
                        and values and isinstance(values[0], str):
                    values = [QLocale().toDouble(v)[0] for v in values]
                elif isinstance(attr, DiscreteVariable):
                    values = [attr.values[i - 1] if i else "" for i in values]
                encoded.append((
                    attr.name,
                    context.attributes.get(attr.name)
                    or context.metas.get(attr.name),
                    op,
                    values
                ))
        return encoded

    # pylint: disable=arguments-differ
    def decode_setting(self, setting, value, domain=None, *_args):
        value = super().decode_setting(setting, value, domain)
        if setting.name == 'conditions':
            CONTINUOUS = vartype(ContinuousVariable("x"))
            # Use this after 2022/2/2:
            # for i, (attr, tpe, op, values) in enumerate(value):
            #     if tpe is not None:
            for i, (attr, *tpe, op, values) in enumerate(value):
                if tpe != [None] \
                        or not tpe and attr not in OWSelectRows.AllTypes:
                    attr = domain[attr]
                # check for exact match, pylint: disable=unidiomatic-typecheck
                if type(attr) is ContinuousVariable \
                        or OWSelectRows.AllTypes.get(attr) == CONTINUOUS:
                    values = [QLocale().toString(float(i), 'f') for i in values]
                elif isinstance(attr, DiscreteVariable):
                    # After 2022/2/2, use just the expression in else clause
                    if values and isinstance(values[0], int):
                        # Backwards compatibility. Reset setting if we detect
                        # that the number of values decreased. Still broken if
                        # they're reordered or we don't detect the decrease.
                        #
                        # indices start with 1, thus >, not >=
                        if max(values) > len(attr.values):
                            values = (0, )
                    else:
                        values = tuple(attr.to_val(val) + 1 if val else 0
                                       for val in values if val in attr.values) \
                                 or (0, )
                value[i] = (attr, op, values)
        return value

    def match(self, context, domain, attrs, metas):
        if (attrs, metas) == (context.attributes, context.metas):
            return self.PERFECT_MATCH

        conditions = context.values["conditions"]
        all_vars = attrs.copy()
        all_vars.update(metas)
        matched = [all_vars.get(name) == tpe  # also matches "all (...)" strings
                   # After 2022/2/2 remove this line:
                   if len(rest) == 2 else name in all_vars
                   for name, tpe, *rest in conditions]
        if any(matched):
            return 0.5 * sum(matched) / len(matched)
        return self.NO_MATCH

    def filter_value(self, setting, data, domain, attrs, metas):
        if setting.name != "conditions":
            super().filter_value(setting, data, domain, attrs, metas)
            return

        all_vars = attrs.copy()
        all_vars.update(metas)
        conditions = data["conditions"]
        # Use this after 2022/2/2: if any(all_vars.get(name) == tpe:
        # conditions[:] = [(name, tpe, *rest) for name, tpe, *rest in conditions
        #                  if all_vars.get(name) == tpe]
        conditions[:] = [
            (name, tpe, *rest) for name, tpe, *rest in conditions
            # all_vars.get(name) == tpe also matches "all (...)" which are
            # encoded with type `None`
            if (all_vars.get(name) == tpe if len(rest) == 2
                else name in all_vars)]


class FilterDiscreteType(enum.Enum):
    # pylint: disable=invalid-name
    Equal = "Equal"
    NotEqual = "NotEqual"
    In = "In"
    IsDefined = "IsDefined"


def _plural(s):
    s = s.replace("is ", "are ")
    for word in ("equals", "contains", "begins", "ends"):
        s = s.replace(word, word[:-1])
    return s


class CalendarWidgetWithTime(QCalendarWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.timeedit = QDateTimeEdit(displayFormat="hh:mm:ss")
        self.timeedit.setTime(self.parent().min_datetime.time())

        self._time_layout = sublay = QHBoxLayout()
        sublay.setContentsMargins(6, 6, 6, 6)
        sublay.addStretch(1)
        sublay.addWidget(QLabel("Time: "))
        sublay.addWidget(self.timeedit)
        sublay.addStretch(1)
        self.layout().addLayout(sublay)

    def minimumSize(self):
        return self.sizeHint()

    def sizeHint(self):
        size = super().sizeHint()
        size.setHeight(
            size.height()
            + self._time_layout.sizeHint().height()
            + self.layout().spacing())
        return size

class OWSelectRows(widget.OWWidget):
    name = "Select Rows"
    id = "Orange.widgets.data.file"
    description = "Select rows from the data based on values of variables."
    icon = "icons/SelectRows.svg"
    priority = 100
    category = "Data"
    keywords = ["filter"]

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

    class Outputs:
        matching_data = Output("Matching Data", Table, default=True)
        unmatched_data = Output("Unmatched Data", Table)
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)

    want_main_area = False

    settingsHandler = SelectRowsContextHandler()
    conditions = ContextSetting([])
    update_on_change = Setting(True)
    purge_attributes = Setting(False, schema_only=True)
    purge_classes = Setting(False, schema_only=True)
    auto_commit = Setting(True)

    settings_version = 2

    Operators = {
        ContinuousVariable: [
            (FilterContinuous.Equal, "equals"),
            (FilterContinuous.NotEqual, "is not"),
            (FilterContinuous.Less, "is below"),
            (FilterContinuous.LessEqual, "is at most"),
            (FilterContinuous.Greater, "is greater than"),
            (FilterContinuous.GreaterEqual, "is at least"),
            (FilterContinuous.Between, "is between"),
            (FilterContinuous.Outside, "is outside"),
            (FilterContinuous.IsDefined, "is defined"),
        ],
        DiscreteVariable: [
            (FilterDiscreteType.Equal, "is"),
            (FilterDiscreteType.NotEqual, "is not"),
            (FilterDiscreteType.In, "is one of"),
            (FilterDiscreteType.IsDefined, "is defined")
        ],
        StringVariable: [
            (FilterString.Equal, "equals"),
            (FilterString.NotEqual, "is not"),
            (FilterString.Less, "is before"),
            (FilterString.LessEqual, "is equal or before"),
            (FilterString.Greater, "is after"),
            (FilterString.GreaterEqual, "is equal or after"),
            (FilterString.Between, "is between"),
            (FilterString.Outside, "is outside"),
            (FilterString.Contains, "contains"),
            (FilterString.StartsWith, "begins with"),
            (FilterString.EndsWith, "ends with"),
            (FilterString.IsDefined, "is defined"),
        ]
    }

    Operators[TimeVariable] = Operators[ContinuousVariable]

    AllTypes = {}
    for _all_name, _all_type, _all_ops in (
            ("All variables", 0,
             [(None, "are defined")]),
            ("All numeric variables", 2,
             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),
            ("All string variables", 3,
             [(v, _plural(t)) for v, t in Operators[StringVariable]])):
        Operators[_all_name] = _all_ops
        AllTypes[_all_name] = _all_type

    operator_names = {vtype: [name for _, name in filters]
                      for vtype, filters in Operators.items()}

    class Error(widget.OWWidget.Error):
        parsing_error = Msg("{}")

    def __init__(self):
        super().__init__()

        self.old_purge_classes = True

        self.conditions = []
        self.last_output_conditions = None
        self.data = None
        self.data_desc = self.match_desc = self.nonmatch_desc = None
        self.variable_model = DomainModel(
            [list(self.AllTypes), DomainModel.Separator,
             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])

        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)
        self.cond_list = QTableWidget(
            box, showGrid=False, selectionMode=QTableWidget.NoSelection)
        box.layout().addWidget(self.cond_list)
        self.cond_list.setColumnCount(4)
        self.cond_list.setRowCount(0)
        self.cond_list.verticalHeader().hide()
        self.cond_list.horizontalHeader().hide()
        for i in range(3):
            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)
        self.cond_list.horizontalHeader().resizeSection(3, 30)
        self.cond_list.viewport().setBackgroundRole(QPalette.Window)

        box2 = gui.hBox(box)
        gui.rubber(box2)
        self.add_button = gui.button(
            box2, self, "Add Condition", callback=self.add_row)
        self.add_all_button = gui.button(
            box2, self, "Add All Variables", callback=self.add_all)
        self.remove_all_button = gui.button(
            box2, self, "Remove All", callback=self.remove_all)
        gui.rubber(box2)

        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())
        layout = boxes.layout()

        box_setting = gui.vBox(boxes, addToLayout=False, box=True)
        self.cb_pa = gui.checkBox(
            box_setting, self, "purge_attributes", "Remove unused features",
            callback=self.conditions_changed)
        gui.separator(box_setting, height=1)
        self.cb_pc = gui.checkBox(
            box_setting, self, "purge_classes", "Remove unused classes",
            callback=self.conditions_changed)
        layout.addWidget(box_setting, 1)

        self.report_button.setFixedWidth(120)
        gui.rubber(self.buttonsArea.layout())
        layout.addWidget(self.buttonsArea)

        acbox = gui.auto_send(None, self, "auto_commit")
        layout.addWidget(acbox, 1)
        layout.setAlignment(acbox, Qt.AlignBottom)

        self.info.set_input_summary(self.info.NoInput)
        self.info.set_output_summary(self.info.NoOutput)

        self.set_data(None)
        self.resize(600, 400)

    def add_row(self, attr=None, condition_type=None, condition_value=None):
        model = self.cond_list.model()
        row = model.rowCount()
        model.insertRow(row)

        attr_combo = ComboBoxSearch(
            minimumContentsLength=12,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)
        attr_combo.setModel(self.variable_model)
        attr_combo.row = row
        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr
                                   else len(self.AllTypes) + 1)
        self.cond_list.setCellWidget(row, 0, attr_combo)

        index = QPersistentModelIndex(model.index(row, 3))
        temp_button = QPushButton('×', self, flat=True,
                                  styleSheet='* {font-size: 16pt; color: silver}'
                                             '*:hover {color: black}')
        temp_button.clicked.connect(lambda: self.remove_one(index.row()))
        self.cond_list.setCellWidget(row, 3, temp_button)

        self.remove_all_button.setDisabled(False)
        self.set_new_operators(attr_combo, attr is not None,
                               condition_type, condition_value)
        attr_combo.currentIndexChanged.connect(
            lambda _: self.set_new_operators(attr_combo, False))

        self.cond_list.resizeRowToContents(row)

    def add_all(self):
        if self.cond_list.rowCount():
            Mb = QMessageBox
            if Mb.question(
                    self, "Remove existing filters",
                    "This will replace the existing filters with "
                    "filters for all variables.", Mb.Ok | Mb.Cancel) != Mb.Ok:
                return
            self.remove_all()
        for attr in self.variable_model[len(self.AllTypes) + 1:]:
            self.add_row(attr)
        self.conditions_changed()

    def remove_one(self, rownum):
        self.remove_one_row(rownum)
        self.conditions_changed()

    def remove_all(self):
        self.remove_all_rows()
        self.conditions_changed()

    def remove_one_row(self, rownum):
        self.cond_list.removeRow(rownum)
        if self.cond_list.model().rowCount() == 0:
            self.remove_all_button.setDisabled(True)

    def remove_all_rows(self):
        # Disconnect signals to avoid stray emits when changing variable_model
        for row in range(self.cond_list.rowCount()):
            for col in (0, 1):
                widg = self.cond_list.cellWidget(row, col)
                if widg:
                    widg.currentIndexChanged.disconnect()
        self.cond_list.clear()
        self.cond_list.setRowCount(0)
        self.remove_all_button.setDisabled(True)

    def set_new_operators(self, attr_combo, adding_all,
                          selected_index=None, selected_values=None):
        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)
        prev_text = old_combo.currentText() if old_combo else ""
        oper_combo = QComboBox()
        oper_combo.row = attr_combo.row
        oper_combo.attr_combo = attr_combo
        attr_name = attr_combo.currentText()
        if attr_name in self.AllTypes:
            oper_combo.addItems(self.operator_names[attr_name])
        else:
            var = self.data.domain[attr_name]
            oper_combo.addItems(self.operator_names[type(var)])
        if selected_index is None:
            selected_index = oper_combo.findText(prev_text)
            if selected_index == -1:
                selected_index = 0
        oper_combo.setCurrentIndex(selected_index)
        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
        self.set_new_values(oper_combo, adding_all, selected_values)
        oper_combo.currentIndexChanged.connect(
            lambda _: self.set_new_values(oper_combo, False))

    @staticmethod
    def _get_lineedit_contents(box):
        contents = []
        for child in getattr(box, "controls", [box]):
            if isinstance(child, QLineEdit):
                contents.append(child.text())
            elif isinstance(child, DateTimeWidget):
                if child.format == (0, 1):
                    contents.append(child.time())
                elif child.format == (1, 0):
                    contents.append(child.date())
                elif child.format == (1, 1):
                    contents.append(child.dateTime())
        return contents

    @staticmethod
    def _get_value_contents(box):
        cont = []
        names = []
        for child in getattr(box, "controls", [box]):
            if isinstance(child, QLineEdit):
                cont.append(child.text())
            elif isinstance(child, QComboBox):
                cont.append(child.currentIndex())
            elif isinstance(child, QToolButton):
                if child.popup is not None:
                    model = child.popup.list_view.model()
                    for row in range(model.rowCount()):
                        item = model.item(row)
                        if item.checkState():
                            cont.append(row + 1)
                            names.append(item.text())
                    child.desc_text = ', '.join(names)
                    child.set_text()
            elif isinstance(child, DateTimeWidget):
                if child.format == (0, 1):
                    cont.append(child.time())
                elif child.format == (1, 0):
                    cont.append(child.date())
                elif child.format == (1, 1):
                    cont.append(child.dateTime())
            elif isinstance(child, QLabel) or child is None:
                pass
            else:
                raise TypeError('Type %s not supported.' % type(child))
        return tuple(cont)

    class QDoubleValidatorEmpty(QDoubleValidator):
        def validate(self, input_, pos):
            if not input_:
                return QDoubleValidator.Acceptable, input_, pos
            if self.locale().groupSeparator() in input_:
                return QDoubleValidator.Invalid, input_, pos
            return super().validate(input_, pos)

    def set_new_values(self, oper_combo, adding_all, selected_values=None):
        # def remove_children():
        #     for child in box.children()[1:]:
        #         box.layout().removeWidget(child)
        #         child.setParent(None)

        def add_textual(contents):
            le = gui.lineEdit(box, self, None,
                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                     QSizePolicy.Expanding))
            if contents:
                le.setText(contents)
            le.setAlignment(Qt.AlignRight)
            le.editingFinished.connect(self.conditions_changed)
            return le

        def add_numeric(contents):
            le = add_textual(contents)
            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())
            return le

        box = self.cond_list.cellWidget(oper_combo.row, 2)
        lc = ["", ""]
        oper = oper_combo.currentIndex()
        attr_name = oper_combo.attr_combo.currentText()
        if attr_name in self.AllTypes:
            vtype = self.AllTypes[attr_name]
            var = None
        else:
            var = self.data.domain[attr_name]
            var_idx = self.data.domain.index(attr_name)
            vtype = vartype(var)
            if selected_values is not None:
                lc = list(selected_values) + ["", ""]
                lc = [str(x) if vtype != 4 else x for x in lc[:2]]
        if box and vtype == box.var_type:
            lc = self._get_lineedit_contents(box) + lc

        if oper_combo.currentText().endswith(" defined"):
            label = QLabel()
            label.var_type = vtype
            self.cond_list.setCellWidget(oper_combo.row, 2, label)
        elif var is not None and var.is_discrete:
            if oper_combo.currentText().endswith(" one of"):
                if selected_values:
                    lc = list(selected_values)
                button = DropDownToolButton(self, var, lc)
                button.var_type = vtype
                self.cond_list.setCellWidget(oper_combo.row, 2, button)
            else:
                combo = ComboBoxSearch()
                combo.addItems(("", ) + var.values)
                if lc[0]:
                    combo.setCurrentIndex(int(lc[0]))
                else:
                    combo.setCurrentIndex(0)
                combo.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, combo)
                combo.currentIndexChanged.connect(self.conditions_changed)
        else:
            box = gui.hBox(self, addToLayout=False)
            box.var_type = vtype
            self.cond_list.setCellWidget(oper_combo.row, 2, box)
            if vtype == 2:  # continuous:
                box.controls = [add_numeric(lc[0])]
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_numeric(lc[1]))
            elif vtype == 3:  # string:
                box.controls = [add_textual(lc[0])]
                if oper in [6, 7]:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_textual(lc[1]))
            elif vtype == 4:  # time:
                def invalidate_datetime():
                    if w_:
                        if w.dateTime() > w_.dateTime():
                            w_.setDateTime(w.dateTime())
                        if w.format == (1, 1):
                            w.calendarWidget.timeedit.setTime(w.time())
                            w_.calendarWidget.timeedit.setTime(w_.time())
                    elif w.format == (1, 1):
                        w.calendarWidget.timeedit.setTime(w.time())

                def datetime_changed():
                    self.conditions_changed()
                    invalidate_datetime()

                datetime_format = (var.have_date, var.have_time)
                column = self.data[:, var_idx]
                w = DateTimeWidget(self, column, datetime_format)
                w.set_datetime(lc[0])
                box.controls = [w]
                box.layout().addWidget(w)
                w.dateTimeChanged.connect(datetime_changed)
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    w_ = DateTimeWidget(self, column, datetime_format)
                    w_.set_datetime(lc[1])
                    box.layout().addWidget(w_)
                    box.controls.append(w_)
                    invalidate_datetime()
                    w_.dateTimeChanged.connect(datetime_changed)
                else:
                    w_ = None
            else:
                box.controls = []
        if not adding_all:
            self.conditions_changed()

    @Inputs.data
    def set_data(self, data):
        self.closeContext()
        self.data = data
        self.cb_pa.setEnabled(not isinstance(data, SqlTable))
        self.cb_pc.setEnabled(not isinstance(data, SqlTable))
        self.remove_all_rows()
        self.add_button.setDisabled(data is None)
        self.add_all_button.setDisabled(
            data is None or
            len(data.domain.variables) + len(data.domain.metas) > 100)
        if not data:
            self.info.set_input_summary(self.info.NoInput)
            self.data_desc = None
            self.variable_model.set_domain(None)
            self.commit()
            return
        self.data_desc = report.describe_data_brief(data)
        self.variable_model.set_domain(data.domain)

        self.conditions = []
        self.openContext(data)
        for attr, cond_type, cond_value in self.conditions:
            if attr in self.variable_model:
                self.add_row(attr, cond_type, cond_value)
        if not self.cond_list.model().rowCount():
            self.add_row()

        self.info.set_input_summary(data.approx_len(),
                                    format_summary_details(data))
        self.unconditional_commit()

    def conditions_changed(self):
        try:
            cells_by_rows = (
                [self.cond_list.cellWidget(row, col) for col in range(3)]
                for row in range(self.cond_list.rowCount())
            )
            self.conditions = [
                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),
                 oper_cell.currentIndex(),
                 self._get_value_contents(val_cell))
                for var_cell, oper_cell, val_cell in cells_by_rows]
            if self.update_on_change and (
                    self.last_output_conditions is None or
                    self.last_output_conditions != self.conditions):
                self.commit()
        except AttributeError:
            # Attribute error appears if the signal is triggered when the
            # controls are being constructed
            pass

    @staticmethod
    def _values_to_floats(attr, values):
        if len(values) == 0:
            return values
        if not all(values):
            return None
        if isinstance(attr, TimeVariable):
            values = (value.toString(format=Qt.ISODate) for value in values)
            parse = lambda x: (attr.parse(x), True)
        else:
            parse = QLocale().toDouble

        try:
            floats, ok = zip(*[parse(v) for v in values])
            if not all(ok):
                raise ValueError('Some values could not be parsed as floats'
                                 'in the current locale: {}'.format(values))
        except TypeError:
            floats = values  # values already floats
        assert all(isinstance(v, float) for v in floats)
        return floats

    def commit(self):
        matching_output = self.data
        non_matching_output = None
        annotated_output = None

        self.Error.clear()
        if self.data:
            domain = self.data.domain
            conditions = []
            for attr_name, oper_idx, values in self.conditions:
                if attr_name in self.AllTypes:
                    attr_index = attr = None
                    attr_type = self.AllTypes[attr_name]
                    operators = self.Operators[attr_name]
                else:
                    attr_index = domain.index(attr_name)
                    attr = domain[attr_index]
                    attr_type = vartype(attr)
                    operators = self.Operators[type(attr)]
                opertype, _ = operators[oper_idx]
                if attr_type == 0:
                    filt = data_filter.IsDefined()
                elif attr_type in (2, 4):  # continuous, time
                    try:
                        floats = self._values_to_floats(attr, values)
                    except ValueError as e:
                        self.Error.parsing_error(e.args[0])
                        return
                    if floats is None:
                        continue
                    filt = data_filter.FilterContinuous(
                        attr_index, opertype, *floats)
                elif attr_type == 3:  # string
                    filt = data_filter.FilterString(
                        attr_index, opertype, *[str(v) for v in values])
                else:
                    if opertype == FilterDiscreteType.IsDefined:
                        f_values = None
                    else:
                        if not values or not values[0]:
                            continue
                        values = [attr.values[i-1] for i in values]
                        if opertype == FilterDiscreteType.Equal:
                            f_values = {values[0]}
                        elif opertype == FilterDiscreteType.NotEqual:
                            f_values = set(attr.values)
                            f_values.remove(values[0])
                        elif opertype == FilterDiscreteType.In:
                            f_values = set(values)
                        else:
                            raise ValueError("invalid operand")
                    filt = data_filter.FilterDiscrete(attr_index, f_values)
                conditions.append(filt)

            if conditions:
                filters = data_filter.Values(conditions)
                matching_output = filters(self.data)
                filters.negate = True
                non_matching_output = filters(self.data)

                row_sel = np.in1d(self.data.ids, matching_output.ids)
                annotated_output = create_annotated_table(self.data, row_sel)

            # if hasattr(self.data, "name"):
            #     matching_output.name = self.data.name
            #     non_matching_output.name = self.data.name

            purge_attrs = self.purge_attributes
            purge_classes = self.purge_classes
            if (purge_attrs or purge_classes) and \
                    not isinstance(self.data, SqlTable):
                attr_flags = sum([Remove.RemoveConstant * purge_attrs,
                                  Remove.RemoveUnusedValues * purge_attrs])
                class_flags = sum([Remove.RemoveConstant * purge_classes,
                                   Remove.RemoveUnusedValues * purge_classes])
                # same settings used for attributes and meta features
                remover = Remove(attr_flags, class_flags, attr_flags)

                matching_output = remover(matching_output)
                non_matching_output = remover(non_matching_output)
                annotated_output = remover(annotated_output)

        if not matching_output:
            matching_output = None
        if not non_matching_output:
            non_matching_output = None
        if not annotated_output:
            annotated_output = None

        self.Outputs.matching_data.send(matching_output)
        self.Outputs.unmatched_data.send(non_matching_output)
        self.Outputs.annotated_data.send(annotated_output)

        self.match_desc = report.describe_data_brief(matching_output)
        self.nonmatch_desc = report.describe_data_brief(non_matching_output)

        summary = matching_output.approx_len() if matching_output else \
            self.info.NoOutput
        details = format_summary_details(matching_output) if matching_output else ""
        self.info.set_output_summary(summary, details)

    def send_report(self):
        if not self.data:
            self.report_paragraph("No data.")
            return

        pdesc = None
        describe_domain = False
        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):
            if not d or not d["Data instances"]:
                continue
            ndesc = d.copy()
            del ndesc["Data instances"]
            if pdesc is not None and pdesc != ndesc:
                describe_domain = True
            pdesc = ndesc

        conditions = []
        for attr, oper, values in self.conditions:
            if isinstance(attr, str):
                attr_name = attr
                var_type = self.AllTypes[attr]
                names = self.operator_names[attr_name]
            else:
                attr_name = attr.name
                var_type = vartype(attr)
                names = self.operator_names[type(attr)]
            name = names[oper]
            if oper == len(names) - 1:
                conditions.append("{} {}".format(attr_name, name))
            elif var_type == 1:  # discrete
                if name == "is one of":
                    valnames = [attr.values[v - 1] for v in values]
                    if not valnames:
                        continue
                    if len(valnames) == 1:
                        valstr = valnames[0]
                    else:
                        valstr = f"{', '.join(valnames[:-1])} or {valnames[-1]}"
                    conditions.append(f"{attr} is {valstr}")
                elif values and values[0]:
                    value = values[0] - 1
                    conditions.append(f"{attr} {name} {attr.values[value]}")
            elif var_type == 3:  # string variable
                conditions.append(
                    f"{attr} {name} {' and '.join(map(repr, values))}")
            elif var_type == 4:  # time
                values = (value.toString(format=Qt.ISODate) for value in values)
                conditions.append(f"{attr} {name} {' and '.join(values)}")
            elif all(x for x in values):  # numeric variable
                conditions.append(f"{attr} {name} {' and '.join(values)}")
        items = OrderedDict()
        if describe_domain:
            items.update(self.data_desc)
        else:
            items["Instances"] = self.data_desc["Data instances"]
        items["Condition"] = " AND ".join(conditions) or "no conditions"
        self.report_items("Data", items)
        if describe_domain:
            self.report_items("Matching data", self.match_desc)
            self.report_items("Non-matching data", self.nonmatch_desc)
        else:
            match_inst = \
                bool(self.match_desc) and \
                self.match_desc["Data instances"]
            nonmatch_inst = \
                bool(self.nonmatch_desc) and \
                self.nonmatch_desc["Data instances"]
            self.report_items(
                "Output",
                (("Matching data",
                  "{} instances".format(match_inst) if match_inst else "None"),
                 ("Non-matching data",
                  nonmatch_inst > 0 and "{} instances".format(nonmatch_inst))))

    # Uncomment this on 2022/2/2
    #
    # @classmethod
    # def migrate_context(cls, context, version):
    #     if not version or version < 2:
    #         # Just remove; can't migrate because variables types are unknown
    #         context.values["conditions"] = []


class CheckBoxPopup(QWidget):
    def __init__(self, var, lc, widget_parent=None, widg=None):
        QWidget.__init__(self)

        self.list_view = QListView()
        text = []
        model = QStandardItemModel(self.list_view)
        for (i, val) in enumerate(var.values):
            item = QStandardItem(val)
            item.setCheckable(True)
            if i + 1 in lc:
                item.setCheckState(Qt.Checked)
                text.append(val)
            model.appendRow(item)
        model.itemChanged.connect(widget_parent.conditions_changed)
        self.list_view.setModel(model)

        layout = QGridLayout(self)
        layout.addWidget(self.list_view)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.adjustSize()
        self.setWindowFlags(Qt.Popup)

        self.widget = widg
        self.widget.desc_text = ', '.join(text)
        self.widget.set_text()

    def moved(self):
        point = self.widget.rect().bottomRight()
        global_point = self.widget.mapToGlobal(point)
        self.move(global_point - QPoint(self.width(), 0))


class DropDownToolButton(QToolButton):
    def __init__(self, parent, var, lc):
        QToolButton.__init__(self, parent)
        self.desc_text = ''
        self.popup = CheckBoxPopup(var, lc, parent, self)
        self.setMenu(QMenu()) # to show arrow
        self.clicked.connect(self.open_popup)

    def open_popup(self):
        self.popup.moved()
        self.popup.show()

    def set_text(self):
        metrics = QFontMetrics(self.font())
        self.setText(metrics.elidedText(self.desc_text, Qt.ElideRight,
                                        self.width() - 15))

    def resizeEvent(self, _):
        self.set_text()


class DateTimeWidget(QDateTimeEdit):
    def __init__(self, parent, column, datetime_format):
        QDateTimeEdit.__init__(self, parent)

        self.format = datetime_format
        self.have_date, self.have_time = datetime_format[0], datetime_format[1]
        self.set_format(column)
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

    def set_format(self, column):
        str_format = Qt.ISODate
        if self.have_date and self.have_time:
            self.setDisplayFormat("yyyy-MM-dd hh:mm:ss")
            c_format = "%Y-%m-%d %H:%M:%S"
            min_datetime, max_datetime = self.find_range(column, c_format)
            self.min_datetime = QDateTime.fromString(min_datetime, str_format)
            self.max_datetime = QDateTime.fromString(max_datetime, str_format)
            self.setCalendarPopup(True)
            self.calendarWidget = CalendarWidgetWithTime(self)
            self.calendarWidget.timeedit.timeChanged.connect(
                self.set_datetime)
            self.setCalendarWidget(self.calendarWidget)
            self.setDateTimeRange(self.min_datetime, self.max_datetime)

        elif self.have_date and not self.have_time:
            self.setDisplayFormat("yyyy-MM-dd")
            self.setCalendarPopup(True)
            min_datetime, max_datetime = self.find_range(column, "%Y-%m-%d")
            self.min_datetime = QDate.fromString(min_datetime, str_format)
            self.max_datetime = QDate.fromString(max_datetime, str_format)
            self.setDateRange(self.min_datetime, self.max_datetime)

        elif not self.have_date and self.have_time:
            self.setDisplayFormat("hh:mm:ss")
            min_datetime, max_datetime = self.find_range(column, "%H:%M:%S")
            self.min_datetime = QTime.fromString(min_datetime, str_format)
            self.max_datetime = QTime.fromString(max_datetime, str_format)
            self.setTimeRange(self.min_datetime, self.max_datetime)

    def set_datetime(self, date_time):
        if not date_time:
            date_time = self.min_datetime
        if self.have_date and self.have_time:
            if isinstance(date_time, QTime):
                self.setDateTime(
                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))
            else:
                self.setDateTime(date_time)
        elif self.have_date and not self.have_time:
            self.setDate(date_time)
        elif not self.have_date and self.have_time:
            self.setTime(date_time)

    @staticmethod
    def find_range(column, convert_format):
        def convert_timestamp(timestamp):
            if timestamp >= 0:
                return datetime.fromtimestamp(timestamp, tz=timezone.utc)
            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \
                       timedelta(seconds=int(timestamp))

        item_list = [item for items in list(column) for item in items]
        min_datetime = convert_timestamp(
            np.nanmin(item_list)).strftime(convert_format)
        max_datetime = convert_timestamp(
            np.nanmax(item_list)).strftime(convert_format)
        return min_datetime, max_datetime


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWSelectRows).run(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:** Selecting Rows Fails When Time Variable is in Metas

**Description:**
When using the Select Rows widget with a time variable included in the metadata, the widget fails to apply the selection criteria and raises an error.

**Example Code:**
```python
from Orange.data import Table, Domain, TimeVariable

# Create a domain with a time variable in metas
domain = Domain([], metas=[TimeVariable("breach_start")])
data = Table.from_table(domain, existing_table)

# Configure the Select Rows widget
widget.set_data(data)
widget.add_row(data.domain.metas[0], operation_index, [QDate(2013, 5, 5)])
widget.conditions_changed()
widget.unconditional_commit()
```

**Expected Behavior:**
The Select Rows widget should correctly handle time variables in the metadata, allowing users to filter rows based on the specified time criteria without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message:
```
ValueError: zero-size array to reduction operation minimum which has no identity
```
This error occurs during the processing of the time variable, preventing the widget from applying the row selection.

[/ISSUE]
{
    "TestOWSelectRows.test_add_all": "PASSED",
    "TestOWSelectRows.test_add_all_cancel": "PASSED",
    "TestOWSelectRows.test_all_numeric_filter_with_c_locale_from_context": "PASSED",
    "TestOWSelectRows.test_all_numeric_filter_with_sl_SI_locale": "PASSED",
    "TestOWSelectRows.test_annotated_data": "PASSED",
    "TestOWSelectRows.test_backward_compat_match_values": "PASSED",
    "TestOWSelectRows.test_calendar_dates": "PASSED",
    "TestOWSelectRows.test_change_var_type": "PASSED",
    "TestOWSelectRows.test_continuous_filter_with_c_locale": "PASSED",
    "TestOWSelectRows.test_continuous_filter_with_sl_SI_locale": "PASSED",
    "TestOWSelectRows.test_filter_cont": "PASSED",
    "TestOWSelectRows.test_filter_disc": "PASSED",
    "TestOWSelectRows.test_filter_str": "PASSED",
    "TestOWSelectRows.test_filter_time": "PASSED",
    "TestOWSelectRows.test_image_export": "PASSED",
    "TestOWSelectRows.test_is_defined_on_continuous_variable": "PASSED",
    "TestOWSelectRows.test_keep_operator": "PASSED",
    "TestOWSelectRows.test_load_settings": "PASSED",
    "TestOWSelectRows.test_meta_setting": "PASSED",
    "TestOWSelectRows.test_msg_base_class": "PASSED",
    "TestOWSelectRows.test_output_filter": "PASSED",
    "TestOWSelectRows.test_partial_match_values": "PASSED",
    "TestOWSelectRows.test_partial_matches": "PASSED",
    "TestOWSelectRows.test_partial_matches_with_missing_vars": "PASSED",
    "TestOWSelectRows.test_purge_discretized": "PASSED",
    "TestOWSelectRows.test_report": "PASSED",
    "TestOWSelectRows.test_restores_continuous_filter_in_c_locale": "PASSED",
    "TestOWSelectRows.test_restores_continuous_filter_in_sl_SI_locale": "PASSED",
    "TestOWSelectRows.test_store_all_numeric_filter_with_c_locale_to_context": "PASSED",
    "TestOWSelectRows.test_store_all_numeric_filter_with_sl_SI_locale_to_context": "PASSED",
    "TestOWSelectRows.test_stores_settings_in_invariant_locale": "PASSED",
    "TestOWSelectRows.test_summary": "PASSED",
    "TestOWSelectRows.test_support_old_settings": "PASSED",
    "test_filename": "ERROR",
    "TestOWSelectRows.test_end_support_for_version_1": "FAILED",
    "TestOWSelectRows.test_minimum_size": "FAILED"
}
diff --git a/Orange/widgets/data/owselectrows.py b/Orange/widgets/data/owselectrows.py
index 124b55ce6..8abb49f47 100644
--- a/Orange/widgets/data/owselectrows.py
+++ b/Orange/widgets/data/owselectrows.py
@@ -554,7 +554,7 @@ class OWSelectRows(widget.OWWidget):
                     invalidate_datetime()
 
                 datetime_format = (var.have_date, var.have_time)
-                column = self.data[:, var_idx]
+                column = self.data.get_column_view(var_idx)[0]
                 w = DateTimeWidget(self, column, datetime_format)
                 w.set_datetime(lc[0])
                 box.controls = [w]
@@ -951,11 +951,10 @@ class DateTimeWidget(QDateTimeEdit):
             return datetime(1970, 1, 1, tzinfo=timezone.utc) + \
                        timedelta(seconds=int(timestamp))
 
-        item_list = [item for items in list(column) for item in items]
         min_datetime = convert_timestamp(
-            np.nanmin(item_list)).strftime(convert_format)
+            np.nanmin(column)).strftime(convert_format)
         max_datetime = convert_timestamp(
-            np.nanmax(item_list)).strftime(convert_format)
+            np.nanmax(column)).strftime(convert_format)
         return min_datetime, max_datetime
 
 

{"Orange/widgets/data/owselectrows.py": "import enum\nfrom collections import OrderedDict\nfrom datetime import datetime, timezone, timedelta\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,\n    QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,\n    QLabel, QHBoxLayout, QDateTimeEdit, QCalendarWidget)\nfrom AnyQt.QtGui import (QDoubleValidator, QStandardItemModel, QStandardItem,\n                         QFontMetrics, QPalette)\nfrom AnyQt.QtCore import Qt, QPoint, QPersistentModelIndex, QLocale, \\\n    QDateTime, QDate, QTime\n\nfrom orangewidget.utils.combobox import ComboBoxSearch\nfrom Orange.widgets.utils.itemmodels import DomainModel\nfrom Orange.data import (\n    ContinuousVariable, DiscreteVariable, StringVariable, TimeVariable, Table)\nimport Orange.data.filter as data_filter\nfrom Orange.data.filter import FilterContinuous, FilterString\nfrom Orange.data.sql.table import SqlTable\nfrom Orange.preprocess import Remove\nfrom Orange.widgets import widget, gui\nfrom Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\nfrom Orange.widgets.utils import vartype\nfrom Orange.widgets import report\nfrom Orange.widgets.widget import Msg\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.state_summary import format_summary_details\n\n\nclass SelectRowsContextHandler(DomainContextHandler):\n    \"\"\"Context handler that filters conditions\"\"\"\n\n    # pylint: disable=arguments-differ\n    def is_valid_item(self, setting, condition, attrs, metas):\n        \"\"\"Return True if condition applies to a variable in given domain.\"\"\"\n        varname, *_ = condition\n        return varname in attrs or varname in metas\n\n    def encode_setting(self, context, setting, value):\n        if setting.name != 'conditions':\n            return super().encode_settings(context, setting, value)\n\n        encoded = []\n        CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n        for attr, op, values in value:\n            if isinstance(attr, str):\n                if OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                # None will match the value returned by all_vars.get\n                encoded.append((attr, None, op, values))\n            else:\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        and values and isinstance(values[0], str):\n                    values = [QLocale().toDouble(v)[0] for v in values]\n                elif isinstance(attr, DiscreteVariable):\n                    values = [attr.values[i - 1] if i else \"\" for i in values]\n                encoded.append((\n                    attr.name,\n                    context.attributes.get(attr.name)\n                    or context.metas.get(attr.name),\n                    op,\n                    values\n                ))\n        return encoded\n\n    # pylint: disable=arguments-differ\n    def decode_setting(self, setting, value, domain=None, *_args):\n        value = super().decode_setting(setting, value, domain)\n        if setting.name == 'conditions':\n            CONTINUOUS = vartype(ContinuousVariable(\"x\"))\n            # Use this after 2022/2/2:\n            # for i, (attr, tpe, op, values) in enumerate(value):\n            #     if tpe is not None:\n            for i, (attr, *tpe, op, values) in enumerate(value):\n                if tpe != [None] \\\n                        or not tpe and attr not in OWSelectRows.AllTypes:\n                    attr = domain[attr]\n                # check for exact match, pylint: disable=unidiomatic-typecheck\n                if type(attr) is ContinuousVariable \\\n                        or OWSelectRows.AllTypes.get(attr) == CONTINUOUS:\n                    values = [QLocale().toString(float(i), 'f') for i in values]\n                elif isinstance(attr, DiscreteVariable):\n                    # After 2022/2/2, use just the expression in else clause\n                    if values and isinstance(values[0], int):\n                        # Backwards compatibility. Reset setting if we detect\n                        # that the number of values decreased. Still broken if\n                        # they're reordered or we don't detect the decrease.\n                        #\n                        # indices start with 1, thus >, not >=\n                        if max(values) > len(attr.values):\n                            values = (0, )\n                    else:\n                        values = tuple(attr.to_val(val) + 1 if val else 0\n                                       for val in values if val in attr.values) \\\n                                 or (0, )\n                value[i] = (attr, op, values)\n        return value\n\n    def match(self, context, domain, attrs, metas):\n        if (attrs, metas) == (context.attributes, context.metas):\n            return self.PERFECT_MATCH\n\n        conditions = context.values[\"conditions\"]\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        matched = [all_vars.get(name) == tpe  # also matches \"all (...)\" strings\n                   # After 2022/2/2 remove this line:\n                   if len(rest) == 2 else name in all_vars\n                   for name, tpe, *rest in conditions]\n        if any(matched):\n            return 0.5 * sum(matched) / len(matched)\n        return self.NO_MATCH\n\n    def filter_value(self, setting, data, domain, attrs, metas):\n        if setting.name != \"conditions\":\n            super().filter_value(setting, data, domain, attrs, metas)\n            return\n\n        all_vars = attrs.copy()\n        all_vars.update(metas)\n        conditions = data[\"conditions\"]\n        # Use this after 2022/2/2: if any(all_vars.get(name) == tpe:\n        # conditions[:] = [(name, tpe, *rest) for name, tpe, *rest in conditions\n        #                  if all_vars.get(name) == tpe]\n        conditions[:] = [\n            (name, tpe, *rest) for name, tpe, *rest in conditions\n            # all_vars.get(name) == tpe also matches \"all (...)\" which are\n            # encoded with type `None`\n            if (all_vars.get(name) == tpe if len(rest) == 2\n                else name in all_vars)]\n\n\nclass FilterDiscreteType(enum.Enum):\n    # pylint: disable=invalid-name\n    Equal = \"Equal\"\n    NotEqual = \"NotEqual\"\n    In = \"In\"\n    IsDefined = \"IsDefined\"\n\n\ndef _plural(s):\n    s = s.replace(\"is \", \"are \")\n    for word in (\"equals\", \"contains\", \"begins\", \"ends\"):\n        s = s.replace(word, word[:-1])\n    return s\n\n\nclass CalendarWidgetWithTime(QCalendarWidget):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.timeedit = QDateTimeEdit(displayFormat=\"hh:mm:ss\")\n        self.timeedit.setTime(self.parent().min_datetime.time())\n\n        self._time_layout = sublay = QHBoxLayout()\n        sublay.setContentsMargins(6, 6, 6, 6)\n        sublay.addStretch(1)\n        sublay.addWidget(QLabel(\"Time: \"))\n        sublay.addWidget(self.timeedit)\n        sublay.addStretch(1)\n        self.layout().addLayout(sublay)\n\n    def minimumSize(self):\n        return self.sizeHint()\n\n    def sizeHint(self):\n        size = super().sizeHint()\n        size.setHeight(\n            size.height()\n            + self._time_layout.sizeHint().height()\n            + self.layout().spacing())\n        return size\n\nclass OWSelectRows(widget.OWWidget):\n    name = \"Select Rows\"\n    id = \"Orange.widgets.data.file\"\n    description = \"Select rows from the data based on values of variables.\"\n    icon = \"icons/SelectRows.svg\"\n    priority = 100\n    category = \"Data\"\n    keywords = [\"filter\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        matching_data = Output(\"Matching Data\", Table, default=True)\n        unmatched_data = Output(\"Unmatched Data\", Table)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    want_main_area = False\n\n    settingsHandler = SelectRowsContextHandler()\n    conditions = ContextSetting([])\n    update_on_change = Setting(True)\n    purge_attributes = Setting(False, schema_only=True)\n    purge_classes = Setting(False, schema_only=True)\n    auto_commit = Setting(True)\n\n    settings_version = 2\n\n    Operators = {\n        ContinuousVariable: [\n            (FilterContinuous.Equal, \"equals\"),\n            (FilterContinuous.NotEqual, \"is not\"),\n            (FilterContinuous.Less, \"is below\"),\n            (FilterContinuous.LessEqual, \"is at most\"),\n            (FilterContinuous.Greater, \"is greater than\"),\n            (FilterContinuous.GreaterEqual, \"is at least\"),\n            (FilterContinuous.Between, \"is between\"),\n            (FilterContinuous.Outside, \"is outside\"),\n            (FilterContinuous.IsDefined, \"is defined\"),\n        ],\n        DiscreteVariable: [\n            (FilterDiscreteType.Equal, \"is\"),\n            (FilterDiscreteType.NotEqual, \"is not\"),\n            (FilterDiscreteType.In, \"is one of\"),\n            (FilterDiscreteType.IsDefined, \"is defined\")\n        ],\n        StringVariable: [\n            (FilterString.Equal, \"equals\"),\n            (FilterString.NotEqual, \"is not\"),\n            (FilterString.Less, \"is before\"),\n            (FilterString.LessEqual, \"is equal or before\"),\n            (FilterString.Greater, \"is after\"),\n            (FilterString.GreaterEqual, \"is equal or after\"),\n            (FilterString.Between, \"is between\"),\n            (FilterString.Outside, \"is outside\"),\n            (FilterString.Contains, \"contains\"),\n            (FilterString.StartsWith, \"begins with\"),\n            (FilterString.EndsWith, \"ends with\"),\n            (FilterString.IsDefined, \"is defined\"),\n        ]\n    }\n\n    Operators[TimeVariable] = Operators[ContinuousVariable]\n\n    AllTypes = {}\n    for _all_name, _all_type, _all_ops in (\n            (\"All variables\", 0,\n             [(None, \"are defined\")]),\n            (\"All numeric variables\", 2,\n             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),\n            (\"All string variables\", 3,\n             [(v, _plural(t)) for v, t in Operators[StringVariable]])):\n        Operators[_all_name] = _all_ops\n        AllTypes[_all_name] = _all_type\n\n    operator_names = {vtype: [name for _, name in filters]\n                      for vtype, filters in Operators.items()}\n\n    class Error(widget.OWWidget.Error):\n        parsing_error = Msg(\"{}\")\n\n    def __init__(self):\n        super().__init__()\n\n        self.old_purge_classes = True\n\n        self.conditions = []\n        self.last_output_conditions = None\n        self.data = None\n        self.data_desc = self.match_desc = self.nonmatch_desc = None\n        self.variable_model = DomainModel(\n            [list(self.AllTypes), DomainModel.Separator,\n             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])\n\n        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)\n        self.cond_list = QTableWidget(\n            box, showGrid=False, selectionMode=QTableWidget.NoSelection)\n        box.layout().addWidget(self.cond_list)\n        self.cond_list.setColumnCount(4)\n        self.cond_list.setRowCount(0)\n        self.cond_list.verticalHeader().hide()\n        self.cond_list.horizontalHeader().hide()\n        for i in range(3):\n            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)\n        self.cond_list.horizontalHeader().resizeSection(3, 30)\n        self.cond_list.viewport().setBackgroundRole(QPalette.Window)\n\n        box2 = gui.hBox(box)\n        gui.rubber(box2)\n        self.add_button = gui.button(\n            box2, self, \"Add Condition\", callback=self.add_row)\n        self.add_all_button = gui.button(\n            box2, self, \"Add All Variables\", callback=self.add_all)\n        self.remove_all_button = gui.button(\n            box2, self, \"Remove All\", callback=self.remove_all)\n        gui.rubber(box2)\n\n        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())\n        layout = boxes.layout()\n\n        box_setting = gui.vBox(boxes, addToLayout=False, box=True)\n        self.cb_pa = gui.checkBox(\n            box_setting, self, \"purge_attributes\", \"Remove unused features\",\n            callback=self.conditions_changed)\n        gui.separator(box_setting, height=1)\n        self.cb_pc = gui.checkBox(\n            box_setting, self, \"purge_classes\", \"Remove unused classes\",\n            callback=self.conditions_changed)\n        layout.addWidget(box_setting, 1)\n\n        self.report_button.setFixedWidth(120)\n        gui.rubber(self.buttonsArea.layout())\n        layout.addWidget(self.buttonsArea)\n\n        acbox = gui.auto_send(None, self, \"auto_commit\")\n        layout.addWidget(acbox, 1)\n        layout.setAlignment(acbox, Qt.AlignBottom)\n\n        self.info.set_input_summary(self.info.NoInput)\n        self.info.set_output_summary(self.info.NoOutput)\n\n        self.set_data(None)\n        self.resize(600, 400)\n\n    def add_row(self, attr=None, condition_type=None, condition_value=None):\n        model = self.cond_list.model()\n        row = model.rowCount()\n        model.insertRow(row)\n\n        attr_combo = ComboBoxSearch(\n            minimumContentsLength=12,\n            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)\n        attr_combo.setModel(self.variable_model)\n        attr_combo.row = row\n        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr\n                                   else len(self.AllTypes) + 1)\n        self.cond_list.setCellWidget(row, 0, attr_combo)\n\n        index = QPersistentModelIndex(model.index(row, 3))\n        temp_button = QPushButton('\u00d7', self, flat=True,\n                                  styleSheet='* {font-size: 16pt; color: silver}'\n                                             '*:hover {color: black}')\n        temp_button.clicked.connect(lambda: self.remove_one(index.row()))\n        self.cond_list.setCellWidget(row, 3, temp_button)\n\n        self.remove_all_button.setDisabled(False)\n        self.set_new_operators(attr_combo, attr is not None,\n                               condition_type, condition_value)\n        attr_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_operators(attr_combo, False))\n\n        self.cond_list.resizeRowToContents(row)\n\n    def add_all(self):\n        if self.cond_list.rowCount():\n            Mb = QMessageBox\n            if Mb.question(\n                    self, \"Remove existing filters\",\n                    \"This will replace the existing filters with \"\n                    \"filters for all variables.\", Mb.Ok | Mb.Cancel) != Mb.Ok:\n                return\n            self.remove_all()\n        for attr in self.variable_model[len(self.AllTypes) + 1:]:\n            self.add_row(attr)\n        self.conditions_changed()\n\n    def remove_one(self, rownum):\n        self.remove_one_row(rownum)\n        self.conditions_changed()\n\n    def remove_all(self):\n        self.remove_all_rows()\n        self.conditions_changed()\n\n    def remove_one_row(self, rownum):\n        self.cond_list.removeRow(rownum)\n        if self.cond_list.model().rowCount() == 0:\n            self.remove_all_button.setDisabled(True)\n\n    def remove_all_rows(self):\n        # Disconnect signals to avoid stray emits when changing variable_model\n        for row in range(self.cond_list.rowCount()):\n            for col in (0, 1):\n                widg = self.cond_list.cellWidget(row, col)\n                if widg:\n                    widg.currentIndexChanged.disconnect()\n        self.cond_list.clear()\n        self.cond_list.setRowCount(0)\n        self.remove_all_button.setDisabled(True)\n\n    def set_new_operators(self, attr_combo, adding_all,\n                          selected_index=None, selected_values=None):\n        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)\n        prev_text = old_combo.currentText() if old_combo else \"\"\n        oper_combo = QComboBox()\n        oper_combo.row = attr_combo.row\n        oper_combo.attr_combo = attr_combo\n        attr_name = attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            oper_combo.addItems(self.operator_names[attr_name])\n        else:\n            var = self.data.domain[attr_name]\n            oper_combo.addItems(self.operator_names[type(var)])\n        if selected_index is None:\n            selected_index = oper_combo.findText(prev_text)\n            if selected_index == -1:\n                selected_index = 0\n        oper_combo.setCurrentIndex(selected_index)\n        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)\n        self.set_new_values(oper_combo, adding_all, selected_values)\n        oper_combo.currentIndexChanged.connect(\n            lambda _: self.set_new_values(oper_combo, False))\n\n    @staticmethod\n    def _get_lineedit_contents(box):\n        contents = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                contents.append(child.text())\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    contents.append(child.time())\n                elif child.format == (1, 0):\n                    contents.append(child.date())\n                elif child.format == (1, 1):\n                    contents.append(child.dateTime())\n        return contents\n\n    @staticmethod\n    def _get_value_contents(box):\n        cont = []\n        names = []\n        for child in getattr(box, \"controls\", [box]):\n            if isinstance(child, QLineEdit):\n                cont.append(child.text())\n            elif isinstance(child, QComboBox):\n                cont.append(child.currentIndex())\n            elif isinstance(child, QToolButton):\n                if child.popup is not None:\n                    model = child.popup.list_view.model()\n                    for row in range(model.rowCount()):\n                        item = model.item(row)\n                        if item.checkState():\n                            cont.append(row + 1)\n                            names.append(item.text())\n                    child.desc_text = ', '.join(names)\n                    child.set_text()\n            elif isinstance(child, DateTimeWidget):\n                if child.format == (0, 1):\n                    cont.append(child.time())\n                elif child.format == (1, 0):\n                    cont.append(child.date())\n                elif child.format == (1, 1):\n                    cont.append(child.dateTime())\n            elif isinstance(child, QLabel) or child is None:\n                pass\n            else:\n                raise TypeError('Type %s not supported.' % type(child))\n        return tuple(cont)\n\n    class QDoubleValidatorEmpty(QDoubleValidator):\n        def validate(self, input_, pos):\n            if not input_:\n                return QDoubleValidator.Acceptable, input_, pos\n            if self.locale().groupSeparator() in input_:\n                return QDoubleValidator.Invalid, input_, pos\n            return super().validate(input_, pos)\n\n    def set_new_values(self, oper_combo, adding_all, selected_values=None):\n        # def remove_children():\n        #     for child in box.children()[1:]:\n        #         box.layout().removeWidget(child)\n        #         child.setParent(None)\n\n        def add_textual(contents):\n            le = gui.lineEdit(box, self, None,\n                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,\n                                                     QSizePolicy.Expanding))\n            if contents:\n                le.setText(contents)\n            le.setAlignment(Qt.AlignRight)\n            le.editingFinished.connect(self.conditions_changed)\n            return le\n\n        def add_numeric(contents):\n            le = add_textual(contents)\n            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())\n            return le\n\n        box = self.cond_list.cellWidget(oper_combo.row, 2)\n        lc = [\"\", \"\"]\n        oper = oper_combo.currentIndex()\n        attr_name = oper_combo.attr_combo.currentText()\n        if attr_name in self.AllTypes:\n            vtype = self.AllTypes[attr_name]\n            var = None\n        else:\n            var = self.data.domain[attr_name]\n            var_idx = self.data.domain.index(attr_name)\n            vtype = vartype(var)\n            if selected_values is not None:\n                lc = list(selected_values) + [\"\", \"\"]\n                lc = [str(x) if vtype != 4 else x for x in lc[:2]]\n        if box and vtype == box.var_type:\n            lc = self._get_lineedit_contents(box) + lc\n\n        if oper_combo.currentText().endswith(\" defined\"):\n            label = QLabel()\n            label.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, label)\n        elif var is not None and var.is_discrete:\n            if oper_combo.currentText().endswith(\" one of\"):\n                if selected_values:\n                    lc = list(selected_values)\n                button = DropDownToolButton(self, var, lc)\n                button.var_type = vtype\n                self.cond_list.setCellWidget(oper_combo.row, 2, button)\n            else:\n                combo = ComboBoxSearch()\n                combo.addItems((\"\", ) + var.values)\n                if lc[0]:\n                    combo.setCurrentIndex(int(lc[0]))\n                else:\n                    combo.setCurrentIndex(0)\n                combo.var_type = vartype(var)\n                self.cond_list.setCellWidget(oper_combo.row, 2, combo)\n                combo.currentIndexChanged.connect(self.conditions_changed)\n        else:\n            box = gui.hBox(self, addToLayout=False)\n            box.var_type = vtype\n            self.cond_list.setCellWidget(oper_combo.row, 2, box)\n            if vtype == 2:  # continuous:\n                box.controls = [add_numeric(lc[0])]\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_numeric(lc[1]))\n            elif vtype == 3:  # string:\n                box.controls = [add_textual(lc[0])]\n                if oper in [6, 7]:\n                    gui.widgetLabel(box, \" and \")\n                    box.controls.append(add_textual(lc[1]))\n            elif vtype == 4:  # time:\n                def invalidate_datetime():\n                    if w_:\n                        if w.dateTime() > w_.dateTime():\n                            w_.setDateTime(w.dateTime())\n                        if w.format == (1, 1):\n                            w.calendarWidget.timeedit.setTime(w.time())\n                            w_.calendarWidget.timeedit.setTime(w_.time())\n                    elif w.format == (1, 1):\n                        w.calendarWidget.timeedit.setTime(w.time())\n\n                def datetime_changed():\n                    self.conditions_changed()\n                    invalidate_datetime()\n\n                datetime_format = (var.have_date, var.have_time)\n                column = self.data[:, var_idx]\n                w = DateTimeWidget(self, column, datetime_format)\n                w.set_datetime(lc[0])\n                box.controls = [w]\n                box.layout().addWidget(w)\n                w.dateTimeChanged.connect(datetime_changed)\n                if oper > 5:\n                    gui.widgetLabel(box, \" and \")\n                    w_ = DateTimeWidget(self, column, datetime_format)\n                    w_.set_datetime(lc[1])\n                    box.layout().addWidget(w_)\n                    box.controls.append(w_)\n                    invalidate_datetime()\n                    w_.dateTimeChanged.connect(datetime_changed)\n                else:\n                    w_ = None\n            else:\n                box.controls = []\n        if not adding_all:\n            self.conditions_changed()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.closeContext()\n        self.data = data\n        self.cb_pa.setEnabled(not isinstance(data, SqlTable))\n        self.cb_pc.setEnabled(not isinstance(data, SqlTable))\n        self.remove_all_rows()\n        self.add_button.setDisabled(data is None)\n        self.add_all_button.setDisabled(\n            data is None or\n            len(data.domain.variables) + len(data.domain.metas) > 100)\n        if not data:\n            self.info.set_input_summary(self.info.NoInput)\n            self.data_desc = None\n            self.variable_model.set_domain(None)\n            self.commit()\n            return\n        self.data_desc = report.describe_data_brief(data)\n        self.variable_model.set_domain(data.domain)\n\n        self.conditions = []\n        self.openContext(data)\n        for attr, cond_type, cond_value in self.conditions:\n            if attr in self.variable_model:\n                self.add_row(attr, cond_type, cond_value)\n        if not self.cond_list.model().rowCount():\n            self.add_row()\n\n        self.info.set_input_summary(data.approx_len(),\n                                    format_summary_details(data))\n        self.unconditional_commit()\n\n    def conditions_changed(self):\n        try:\n            cells_by_rows = (\n                [self.cond_list.cellWidget(row, col) for col in range(3)]\n                for row in range(self.cond_list.rowCount())\n            )\n            self.conditions = [\n                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),\n                 oper_cell.currentIndex(),\n                 self._get_value_contents(val_cell))\n                for var_cell, oper_cell, val_cell in cells_by_rows]\n            if self.update_on_change and (\n                    self.last_output_conditions is None or\n                    self.last_output_conditions != self.conditions):\n                self.commit()\n        except AttributeError:\n            # Attribute error appears if the signal is triggered when the\n            # controls are being constructed\n            pass\n\n    @staticmethod\n    def _values_to_floats(attr, values):\n        if len(values) == 0:\n            return values\n        if not all(values):\n            return None\n        if isinstance(attr, TimeVariable):\n            values = (value.toString(format=Qt.ISODate) for value in values)\n            parse = lambda x: (attr.parse(x), True)\n        else:\n            parse = QLocale().toDouble\n\n        try:\n            floats, ok = zip(*[parse(v) for v in values])\n            if not all(ok):\n                raise ValueError('Some values could not be parsed as floats'\n                                 'in the current locale: {}'.format(values))\n        except TypeError:\n            floats = values  # values already floats\n        assert all(isinstance(v, float) for v in floats)\n        return floats\n\n    def commit(self):\n        matching_output = self.data\n        non_matching_output = None\n        annotated_output = None\n\n        self.Error.clear()\n        if self.data:\n            domain = self.data.domain\n            conditions = []\n            for attr_name, oper_idx, values in self.conditions:\n                if attr_name in self.AllTypes:\n                    attr_index = attr = None\n                    attr_type = self.AllTypes[attr_name]\n                    operators = self.Operators[attr_name]\n                else:\n                    attr_index = domain.index(attr_name)\n                    attr = domain[attr_index]\n                    attr_type = vartype(attr)\n                    operators = self.Operators[type(attr)]\n                opertype, _ = operators[oper_idx]\n                if attr_type == 0:\n                    filt = data_filter.IsDefined()\n                elif attr_type in (2, 4):  # continuous, time\n                    try:\n                        floats = self._values_to_floats(attr, values)\n                    except ValueError as e:\n                        self.Error.parsing_error(e.args[0])\n                        return\n                    if floats is None:\n                        continue\n                    filt = data_filter.FilterContinuous(\n                        attr_index, opertype, *floats)\n                elif attr_type == 3:  # string\n                    filt = data_filter.FilterString(\n                        attr_index, opertype, *[str(v) for v in values])\n                else:\n                    if opertype == FilterDiscreteType.IsDefined:\n                        f_values = None\n                    else:\n                        if not values or not values[0]:\n                            continue\n                        values = [attr.values[i-1] for i in values]\n                        if opertype == FilterDiscreteType.Equal:\n                            f_values = {values[0]}\n                        elif opertype == FilterDiscreteType.NotEqual:\n                            f_values = set(attr.values)\n                            f_values.remove(values[0])\n                        elif opertype == FilterDiscreteType.In:\n                            f_values = set(values)\n                        else:\n                            raise ValueError(\"invalid operand\")\n                    filt = data_filter.FilterDiscrete(attr_index, f_values)\n                conditions.append(filt)\n\n            if conditions:\n                filters = data_filter.Values(conditions)\n                matching_output = filters(self.data)\n                filters.negate = True\n                non_matching_output = filters(self.data)\n\n                row_sel = np.in1d(self.data.ids, matching_output.ids)\n                annotated_output = create_annotated_table(self.data, row_sel)\n\n            # if hasattr(self.data, \"name\"):\n            #     matching_output.name = self.data.name\n            #     non_matching_output.name = self.data.name\n\n            purge_attrs = self.purge_attributes\n            purge_classes = self.purge_classes\n            if (purge_attrs or purge_classes) and \\\n                    not isinstance(self.data, SqlTable):\n                attr_flags = sum([Remove.RemoveConstant * purge_attrs,\n                                  Remove.RemoveUnusedValues * purge_attrs])\n                class_flags = sum([Remove.RemoveConstant * purge_classes,\n                                   Remove.RemoveUnusedValues * purge_classes])\n                # same settings used for attributes and meta features\n                remover = Remove(attr_flags, class_flags, attr_flags)\n\n                matching_output = remover(matching_output)\n                non_matching_output = remover(non_matching_output)\n                annotated_output = remover(annotated_output)\n\n        if not matching_output:\n            matching_output = None\n        if not non_matching_output:\n            non_matching_output = None\n        if not annotated_output:\n            annotated_output = None\n\n        self.Outputs.matching_data.send(matching_output)\n        self.Outputs.unmatched_data.send(non_matching_output)\n        self.Outputs.annotated_data.send(annotated_output)\n\n        self.match_desc = report.describe_data_brief(matching_output)\n        self.nonmatch_desc = report.describe_data_brief(non_matching_output)\n\n        summary = matching_output.approx_len() if matching_output else \\\n            self.info.NoOutput\n        details = format_summary_details(matching_output) if matching_output else \"\"\n        self.info.set_output_summary(summary, details)\n\n    def send_report(self):\n        if not self.data:\n            self.report_paragraph(\"No data.\")\n            return\n\n        pdesc = None\n        describe_domain = False\n        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):\n            if not d or not d[\"Data instances\"]:\n                continue\n            ndesc = d.copy()\n            del ndesc[\"Data instances\"]\n            if pdesc is not None and pdesc != ndesc:\n                describe_domain = True\n            pdesc = ndesc\n\n        conditions = []\n        for attr, oper, values in self.conditions:\n            if isinstance(attr, str):\n                attr_name = attr\n                var_type = self.AllTypes[attr]\n                names = self.operator_names[attr_name]\n            else:\n                attr_name = attr.name\n                var_type = vartype(attr)\n                names = self.operator_names[type(attr)]\n            name = names[oper]\n            if oper == len(names) - 1:\n                conditions.append(\"{} {}\".format(attr_name, name))\n            elif var_type == 1:  # discrete\n                if name == \"is one of\":\n                    valnames = [attr.values[v - 1] for v in values]\n                    if not valnames:\n                        continue\n                    if len(valnames) == 1:\n                        valstr = valnames[0]\n                    else:\n                        valstr = f\"{', '.join(valnames[:-1])} or {valnames[-1]}\"\n                    conditions.append(f\"{attr} is\u00a0{valstr}\")\n                elif values and values[0]:\n                    value = values[0] - 1\n                    conditions.append(f\"{attr} {name}\u00a0{attr.values[value]}\")\n            elif var_type == 3:  # string variable\n                conditions.append(\n                    f\"{attr} {name}\u00a0{' and '.join(map(repr, values))}\")\n            elif var_type == 4:  # time\n                values = (value.toString(format=Qt.ISODate) for value in values)\n                conditions.append(f\"{attr} {name}\u00a0{' and '.join(values)}\")\n            elif all(x for x in values):  # numeric variable\n                conditions.append(f\"{attr} {name}\u00a0{' and '.join(values)}\")\n        items = OrderedDict()\n        if describe_domain:\n            items.update(self.data_desc)\n        else:\n            items[\"Instances\"] = self.data_desc[\"Data instances\"]\n        items[\"Condition\"] = \" AND \".join(conditions) or \"no conditions\"\n        self.report_items(\"Data\", items)\n        if describe_domain:\n            self.report_items(\"Matching data\", self.match_desc)\n            self.report_items(\"Non-matching data\", self.nonmatch_desc)\n        else:\n            match_inst = \\\n                bool(self.match_desc) and \\\n                self.match_desc[\"Data instances\"]\n            nonmatch_inst = \\\n                bool(self.nonmatch_desc) and \\\n                self.nonmatch_desc[\"Data instances\"]\n            self.report_items(\n                \"Output\",\n                ((\"Matching data\",\n                  \"{} instances\".format(match_inst) if match_inst else \"None\"),\n                 (\"Non-matching data\",\n                  nonmatch_inst > 0 and \"{} instances\".format(nonmatch_inst))))\n\n    # Uncomment this on 2022/2/2\n    #\n    # @classmethod\n    # def migrate_context(cls, context, version):\n    #     if not version or version < 2:\n    #         # Just remove; can't migrate because variables types are unknown\n    #         context.values[\"conditions\"] = []\n\n\nclass CheckBoxPopup(QWidget):\n    def __init__(self, var, lc, widget_parent=None, widg=None):\n        QWidget.__init__(self)\n\n        self.list_view = QListView()\n        text = []\n        model = QStandardItemModel(self.list_view)\n        for (i, val) in enumerate(var.values):\n            item = QStandardItem(val)\n            item.setCheckable(True)\n            if i + 1 in lc:\n                item.setCheckState(Qt.Checked)\n                text.append(val)\n            model.appendRow(item)\n        model.itemChanged.connect(widget_parent.conditions_changed)\n        self.list_view.setModel(model)\n\n        layout = QGridLayout(self)\n        layout.addWidget(self.list_view)\n        layout.setContentsMargins(0, 0, 0, 0)\n        self.setLayout(layout)\n\n        self.adjustSize()\n        self.setWindowFlags(Qt.Popup)\n\n        self.widget = widg\n        self.widget.desc_text = ', '.join(text)\n        self.widget.set_text()\n\n    def moved(self):\n        point = self.widget.rect().bottomRight()\n        global_point = self.widget.mapToGlobal(point)\n        self.move(global_point - QPoint(self.width(), 0))\n\n\nclass DropDownToolButton(QToolButton):\n    def __init__(self, parent, var, lc):\n        QToolButton.__init__(self, parent)\n        self.desc_text = ''\n        self.popup = CheckBoxPopup(var, lc, parent, self)\n        self.setMenu(QMenu()) # to show arrow\n        self.clicked.connect(self.open_popup)\n\n    def open_popup(self):\n        self.popup.moved()\n        self.popup.show()\n\n    def set_text(self):\n        metrics = QFontMetrics(self.font())\n        self.setText(metrics.elidedText(self.desc_text, Qt.ElideRight,\n                                        self.width() - 15))\n\n    def resizeEvent(self, _):\n        self.set_text()\n\n\nclass DateTimeWidget(QDateTimeEdit):\n    def __init__(self, parent, column, datetime_format):\n        QDateTimeEdit.__init__(self, parent)\n\n        self.format = datetime_format\n        self.have_date, self.have_time = datetime_format[0], datetime_format[1]\n        self.set_format(column)\n        self.setSizePolicy(\n            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))\n\n    def set_format(self, column):\n        str_format = Qt.ISODate\n        if self.have_date and self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd hh:mm:ss\")\n            c_format = \"%Y-%m-%d %H:%M:%S\"\n            min_datetime, max_datetime = self.find_range(column, c_format)\n            self.min_datetime = QDateTime.fromString(min_datetime, str_format)\n            self.max_datetime = QDateTime.fromString(max_datetime, str_format)\n            self.setCalendarPopup(True)\n            self.calendarWidget = CalendarWidgetWithTime(self)\n            self.calendarWidget.timeedit.timeChanged.connect(\n                self.set_datetime)\n            self.setCalendarWidget(self.calendarWidget)\n            self.setDateTimeRange(self.min_datetime, self.max_datetime)\n\n        elif self.have_date and not self.have_time:\n            self.setDisplayFormat(\"yyyy-MM-dd\")\n            self.setCalendarPopup(True)\n            min_datetime, max_datetime = self.find_range(column, \"%Y-%m-%d\")\n            self.min_datetime = QDate.fromString(min_datetime, str_format)\n            self.max_datetime = QDate.fromString(max_datetime, str_format)\n            self.setDateRange(self.min_datetime, self.max_datetime)\n\n        elif not self.have_date and self.have_time:\n            self.setDisplayFormat(\"hh:mm:ss\")\n            min_datetime, max_datetime = self.find_range(column, \"%H:%M:%S\")\n            self.min_datetime = QTime.fromString(min_datetime, str_format)\n            self.max_datetime = QTime.fromString(max_datetime, str_format)\n            self.setTimeRange(self.min_datetime, self.max_datetime)\n\n    def set_datetime(self, date_time):\n        if not date_time:\n            date_time = self.min_datetime\n        if self.have_date and self.have_time:\n            if isinstance(date_time, QTime):\n                self.setDateTime(\n                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))\n            else:\n                self.setDateTime(date_time)\n        elif self.have_date and not self.have_time:\n            self.setDate(date_time)\n        elif not self.have_date and self.have_time:\n            self.setTime(date_time)\n\n    @staticmethod\n    def find_range(column, convert_format):\n        def convert_timestamp(timestamp):\n            if timestamp >= 0:\n                return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \\\n                       timedelta(seconds=int(timestamp))\n\n        item_list = [item for items in list(column) for item in items]\n        min_datetime = convert_timestamp(\n            np.nanmin(item_list)).strftime(convert_format)\n        max_datetime = convert_timestamp(\n            np.nanmax(item_list)).strftime(convert_format)\n        return min_datetime, max_datetime\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWSelectRows).run(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:** Selecting Rows Fails When Time Variable is in Metas

**Description:**
When using the Select Rows widget with a time variable included in the metadata, the widget fails to apply the selection criteria and raises an error.

**Example Code:**
```python
from Orange.data import Table, Domain, TimeVariable

# Create a domain with a time variable in metas
domain = Domain([], metas=[TimeVariable("breach_start")])
data = Table.from_table(domain, existing_table)

# Configure the Select Rows widget
widget.set_data(data)
widget.add_row(data.domain.metas[0], operation_index, [QDate(2013, 5, 5)])
widget.conditions_changed()
widget.unconditional_commit()
```

**Expected Behavior:**
The Select Rows widget should correctly handle time variables in the metadata, allowing users to filter rows based on the specified time criteria without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message:
```
ValueError: zero-size array to reduction operation minimum which has no identity
```
This error occurs during the processing of the time variable, preventing the widget from applying the row selection.

[/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/owselectrows.py
import enum
from collections import OrderedDict
from datetime import datetime, timezone, timedelta

import numpy as np

from AnyQt.QtWidgets import (
    QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,
    QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,
    QLabel, QHBoxLayout, QDateTimeEdit, QCalendarWidget)
from AnyQt.QtGui import (QDoubleValidator, QStandardItemModel, QStandardItem,
                         QFontMetrics, QPalette)
from AnyQt.QtCore import Qt, QPoint, QPersistentModelIndex, QLocale, \
    QDateTime, QDate, QTime

from orangewidget.utils.combobox import ComboBoxSearch
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.data import (
    ContinuousVariable, DiscreteVariable, StringVariable, TimeVariable, Table)
import Orange.data.filter as data_filter
from Orange.data.filter import FilterContinuous, FilterString
from Orange.data.sql.table import SqlTable
from Orange.preprocess import Remove
from Orange.widgets import widget, gui
from Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output
from Orange.widgets.utils import vartype
from Orange.widgets import report
from Orange.widgets.widget import Msg
from Orange.widgets.utils.annotated_data import (create_annotated_table,
                                                 ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.utils.state_summary import format_summary_details


class SelectRowsContextHandler(DomainContextHandler):
    """Context handler that filters conditions"""

    # pylint: disable=arguments-differ
    def is_valid_item(self, setting, condition, attrs, metas):
        """Return True if condition applies to a variable in given domain."""
        varname, *_ = condition
        return varname in attrs or varname in metas

    def encode_setting(self, context, setting, value):
        if setting.name != 'conditions':
            return super().encode_settings(context, setting, value)

        encoded = []
        CONTINUOUS = vartype(ContinuousVariable("x"))
        for attr, op, values in value:
            if isinstance(attr, str):
                if OWSelectRows.AllTypes.get(attr) == CONTINUOUS:
                    values = [QLocale().toDouble(v)[0] for v in values]
                # None will match the value returned by all_vars.get
                encoded.append((attr, None, op, values))
            else:
                # check for exact match, pylint: disable=unidiomatic-typecheck
                if type(attr) is ContinuousVariable \
                        and values and isinstance(values[0], str):
                    values = [QLocale().toDouble(v)[0] for v in values]
                elif isinstance(attr, DiscreteVariable):
                    values = [attr.values[i - 1] if i else "" for i in values]
                encoded.append((
                    attr.name,
                    context.attributes.get(attr.name)
                    or context.metas.get(attr.name),
                    op,
                    values
                ))
        return encoded

    # pylint: disable=arguments-differ
    def decode_setting(self, setting, value, domain=None, *_args):
        value = super().decode_setting(setting, value, domain)
        if setting.name == 'conditions':
            CONTINUOUS = vartype(ContinuousVariable("x"))
            # Use this after 2022/2/2:
            # for i, (attr, tpe, op, values) in enumerate(value):
            #     if tpe is not None:
            for i, (attr, *tpe, op, values) in enumerate(value):
                if tpe != [None] \
                        or not tpe and attr not in OWSelectRows.AllTypes:
                    attr = domain[attr]
                # check for exact match, pylint: disable=unidiomatic-typecheck
                if type(attr) is ContinuousVariable \
                        or OWSelectRows.AllTypes.get(attr) == CONTINUOUS:
                    values = [QLocale().toString(float(i), 'f') for i in values]
                elif isinstance(attr, DiscreteVariable):
                    # After 2022/2/2, use just the expression in else clause
                    if values and isinstance(values[0], int):
                        # Backwards compatibility. Reset setting if we detect
                        # that the number of values decreased. Still broken if
                        # they're reordered or we don't detect the decrease.
                        #
                        # indices start with 1, thus >, not >=
                        if max(values) > len(attr.values):
                            values = (0, )
                    else:
                        values = tuple(attr.to_val(val) + 1 if val else 0
                                       for val in values if val in attr.values) \
                                 or (0, )
                value[i] = (attr, op, values)
        return value

    def match(self, context, domain, attrs, metas):
        if (attrs, metas) == (context.attributes, context.metas):
            return self.PERFECT_MATCH

        conditions = context.values["conditions"]
        all_vars = attrs.copy()
        all_vars.update(metas)
        matched = [all_vars.get(name) == tpe  # also matches "all (...)" strings
                   # After 2022/2/2 remove this line:
                   if len(rest) == 2 else name in all_vars
                   for name, tpe, *rest in conditions]
        if any(matched):
            return 0.5 * sum(matched) / len(matched)
        return self.NO_MATCH

    def filter_value(self, setting, data, domain, attrs, metas):
        if setting.name != "conditions":
            super().filter_value(setting, data, domain, attrs, metas)
            return

        all_vars = attrs.copy()
        all_vars.update(metas)
        conditions = data["conditions"]
        # Use this after 2022/2/2: if any(all_vars.get(name) == tpe:
        # conditions[:] = [(name, tpe, *rest) for name, tpe, *rest in conditions
        #                  if all_vars.get(name) == tpe]
        conditions[:] = [
            (name, tpe, *rest) for name, tpe, *rest in conditions
            # all_vars.get(name) == tpe also matches "all (...)" which are
            # encoded with type `None`
            if (all_vars.get(name) == tpe if len(rest) == 2
                else name in all_vars)]


class FilterDiscreteType(enum.Enum):
    # pylint: disable=invalid-name
    Equal = "Equal"
    NotEqual = "NotEqual"
    In = "In"
    IsDefined = "IsDefined"


def _plural(s):
    s = s.replace("is ", "are ")
    for word in ("equals", "contains", "begins", "ends"):
        s = s.replace(word, word[:-1])
    return s


class CalendarWidgetWithTime(QCalendarWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.timeedit = QDateTimeEdit(displayFormat="hh:mm:ss")
        self.timeedit.setTime(self.parent().min_datetime.time())

        self._time_layout = sublay = QHBoxLayout()
        sublay.setContentsMargins(6, 6, 6, 6)
        sublay.addStretch(1)
        sublay.addWidget(QLabel("Time: "))
        sublay.addWidget(self.timeedit)
        sublay.addStretch(1)
        self.layout().addLayout(sublay)

    def minimumSize(self):
        return self.sizeHint()

    def sizeHint(self):
        size = super().sizeHint()
        size.setHeight(
            size.height()
            + self._time_layout.sizeHint().height()
            + self.layout().spacing())
        return size

class OWSelectRows(widget.OWWidget):
    name = "Select Rows"
    id = "Orange.widgets.data.file"
    description = "Select rows from the data based on values of variables."
    icon = "icons/SelectRows.svg"
    priority = 100
    category = "Data"
    keywords = ["filter"]

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

    class Outputs:
        matching_data = Output("Matching Data", Table, default=True)
        unmatched_data = Output("Unmatched Data", Table)
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)

    want_main_area = False

    settingsHandler = SelectRowsContextHandler()
    conditions = ContextSetting([])
    update_on_change = Setting(True)
    purge_attributes = Setting(False, schema_only=True)
    purge_classes = Setting(False, schema_only=True)
    auto_commit = Setting(True)

    settings_version = 2

    Operators = {
        ContinuousVariable: [
            (FilterContinuous.Equal, "equals"),
            (FilterContinuous.NotEqual, "is not"),
            (FilterContinuous.Less, "is below"),
            (FilterContinuous.LessEqual, "is at most"),
            (FilterContinuous.Greater, "is greater than"),
            (FilterContinuous.GreaterEqual, "is at least"),
            (FilterContinuous.Between, "is between"),
            (FilterContinuous.Outside, "is outside"),
            (FilterContinuous.IsDefined, "is defined"),
        ],
        DiscreteVariable: [
            (FilterDiscreteType.Equal, "is"),
            (FilterDiscreteType.NotEqual, "is not"),
            (FilterDiscreteType.In, "is one of"),
            (FilterDiscreteType.IsDefined, "is defined")
        ],
        StringVariable: [
            (FilterString.Equal, "equals"),
            (FilterString.NotEqual, "is not"),
            (FilterString.Less, "is before"),
            (FilterString.LessEqual, "is equal or before"),
            (FilterString.Greater, "is after"),
            (FilterString.GreaterEqual, "is equal or after"),
            (FilterString.Between, "is between"),
            (FilterString.Outside, "is outside"),
            (FilterString.Contains, "contains"),
            (FilterString.StartsWith, "begins with"),
            (FilterString.EndsWith, "ends with"),
            (FilterString.IsDefined, "is defined"),
        ]
    }

    Operators[TimeVariable] = Operators[ContinuousVariable]

    AllTypes = {}
    for _all_name, _all_type, _all_ops in (
            ("All variables", 0,
             [(None, "are defined")]),
            ("All numeric variables", 2,
             [(v, _plural(t)) for v, t in Operators[ContinuousVariable]]),
            ("All string variables", 3,
             [(v, _plural(t)) for v, t in Operators[StringVariable]])):
        Operators[_all_name] = _all_ops
        AllTypes[_all_name] = _all_type

    operator_names = {vtype: [name for _, name in filters]
                      for vtype, filters in Operators.items()}

    class Error(widget.OWWidget.Error):
        parsing_error = Msg("{}")

    def __init__(self):
        super().__init__()

        self.old_purge_classes = True

        self.conditions = []
        self.last_output_conditions = None
        self.data = None
        self.data_desc = self.match_desc = self.nonmatch_desc = None
        self.variable_model = DomainModel(
            [list(self.AllTypes), DomainModel.Separator,
             DomainModel.CLASSES, DomainModel.ATTRIBUTES, DomainModel.METAS])

        box = gui.vBox(self.controlArea, 'Conditions', stretch=100)
        self.cond_list = QTableWidget(
            box, showGrid=False, selectionMode=QTableWidget.NoSelection)
        box.layout().addWidget(self.cond_list)
        self.cond_list.setColumnCount(4)
        self.cond_list.setRowCount(0)
        self.cond_list.verticalHeader().hide()
        self.cond_list.horizontalHeader().hide()
        for i in range(3):
            self.cond_list.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)
        self.cond_list.horizontalHeader().resizeSection(3, 30)
        self.cond_list.viewport().setBackgroundRole(QPalette.Window)

        box2 = gui.hBox(box)
        gui.rubber(box2)
        self.add_button = gui.button(
            box2, self, "Add Condition", callback=self.add_row)
        self.add_all_button = gui.button(
            box2, self, "Add All Variables", callback=self.add_all)
        self.remove_all_button = gui.button(
            box2, self, "Remove All", callback=self.remove_all)
        gui.rubber(box2)

        boxes = gui.widgetBox(self.controlArea, orientation=QHBoxLayout())
        layout = boxes.layout()

        box_setting = gui.vBox(boxes, addToLayout=False, box=True)
        self.cb_pa = gui.checkBox(
            box_setting, self, "purge_attributes", "Remove unused features",
            callback=self.conditions_changed)
        gui.separator(box_setting, height=1)
        self.cb_pc = gui.checkBox(
            box_setting, self, "purge_classes", "Remove unused classes",
            callback=self.conditions_changed)
        layout.addWidget(box_setting, 1)

        self.report_button.setFixedWidth(120)
        gui.rubber(self.buttonsArea.layout())
        layout.addWidget(self.buttonsArea)

        acbox = gui.auto_send(None, self, "auto_commit")
        layout.addWidget(acbox, 1)
        layout.setAlignment(acbox, Qt.AlignBottom)

        self.info.set_input_summary(self.info.NoInput)
        self.info.set_output_summary(self.info.NoOutput)

        self.set_data(None)
        self.resize(600, 400)

    def add_row(self, attr=None, condition_type=None, condition_value=None):
        model = self.cond_list.model()
        row = model.rowCount()
        model.insertRow(row)

        attr_combo = ComboBoxSearch(
            minimumContentsLength=12,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon)
        attr_combo.setModel(self.variable_model)
        attr_combo.row = row
        attr_combo.setCurrentIndex(self.variable_model.indexOf(attr) if attr
                                   else len(self.AllTypes) + 1)
        self.cond_list.setCellWidget(row, 0, attr_combo)

        index = QPersistentModelIndex(model.index(row, 3))
        temp_button = QPushButton('×', self, flat=True,
                                  styleSheet='* {font-size: 16pt; color: silver}'
                                             '*:hover {color: black}')
        temp_button.clicked.connect(lambda: self.remove_one(index.row()))
        self.cond_list.setCellWidget(row, 3, temp_button)

        self.remove_all_button.setDisabled(False)
        self.set_new_operators(attr_combo, attr is not None,
                               condition_type, condition_value)
        attr_combo.currentIndexChanged.connect(
            lambda _: self.set_new_operators(attr_combo, False))

        self.cond_list.resizeRowToContents(row)

    def add_all(self):
        if self.cond_list.rowCount():
            Mb = QMessageBox
            if Mb.question(
                    self, "Remove existing filters",
                    "This will replace the existing filters with "
                    "filters for all variables.", Mb.Ok | Mb.Cancel) != Mb.Ok:
                return
            self.remove_all()
        for attr in self.variable_model[len(self.AllTypes) + 1:]:
            self.add_row(attr)
        self.conditions_changed()

    def remove_one(self, rownum):
        self.remove_one_row(rownum)
        self.conditions_changed()

    def remove_all(self):
        self.remove_all_rows()
        self.conditions_changed()

    def remove_one_row(self, rownum):
        self.cond_list.removeRow(rownum)
        if self.cond_list.model().rowCount() == 0:
            self.remove_all_button.setDisabled(True)

    def remove_all_rows(self):
        # Disconnect signals to avoid stray emits when changing variable_model
        for row in range(self.cond_list.rowCount()):
            for col in (0, 1):
                widg = self.cond_list.cellWidget(row, col)
                if widg:
                    widg.currentIndexChanged.disconnect()
        self.cond_list.clear()
        self.cond_list.setRowCount(0)
        self.remove_all_button.setDisabled(True)

    def set_new_operators(self, attr_combo, adding_all,
                          selected_index=None, selected_values=None):
        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)
        prev_text = old_combo.currentText() if old_combo else ""
        oper_combo = QComboBox()
        oper_combo.row = attr_combo.row
        oper_combo.attr_combo = attr_combo
        attr_name = attr_combo.currentText()
        if attr_name in self.AllTypes:
            oper_combo.addItems(self.operator_names[attr_name])
        else:
            var = self.data.domain[attr_name]
            oper_combo.addItems(self.operator_names[type(var)])
        if selected_index is None:
            selected_index = oper_combo.findText(prev_text)
            if selected_index == -1:
                selected_index = 0
        oper_combo.setCurrentIndex(selected_index)
        self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
        self.set_new_values(oper_combo, adding_all, selected_values)
        oper_combo.currentIndexChanged.connect(
            lambda _: self.set_new_values(oper_combo, False))

    @staticmethod
    def _get_lineedit_contents(box):
        contents = []
        for child in getattr(box, "controls", [box]):
            if isinstance(child, QLineEdit):
                contents.append(child.text())
            elif isinstance(child, DateTimeWidget):
                if child.format == (0, 1):
                    contents.append(child.time())
                elif child.format == (1, 0):
                    contents.append(child.date())
                elif child.format == (1, 1):
                    contents.append(child.dateTime())
        return contents

    @staticmethod
    def _get_value_contents(box):
        cont = []
        names = []
        for child in getattr(box, "controls", [box]):
            if isinstance(child, QLineEdit):
                cont.append(child.text())
            elif isinstance(child, QComboBox):
                cont.append(child.currentIndex())
            elif isinstance(child, QToolButton):
                if child.popup is not None:
                    model = child.popup.list_view.model()
                    for row in range(model.rowCount()):
                        item = model.item(row)
                        if item.checkState():
                            cont.append(row + 1)
                            names.append(item.text())
                    child.desc_text = ', '.join(names)
                    child.set_text()
            elif isinstance(child, DateTimeWidget):
                if child.format == (0, 1):
                    cont.append(child.time())
                elif child.format == (1, 0):
                    cont.append(child.date())
                elif child.format == (1, 1):
                    cont.append(child.dateTime())
            elif isinstance(child, QLabel) or child is None:
                pass
            else:
                raise TypeError('Type %s not supported.' % type(child))
        return tuple(cont)

    class QDoubleValidatorEmpty(QDoubleValidator):
        def validate(self, input_, pos):
            if not input_:
                return QDoubleValidator.Acceptable, input_, pos
            if self.locale().groupSeparator() in input_:
                return QDoubleValidator.Invalid, input_, pos
            return super().validate(input_, pos)

    def set_new_values(self, oper_combo, adding_all, selected_values=None):
        # def remove_children():
        #     for child in box.children()[1:]:
        #         box.layout().removeWidget(child)
        #         child.setParent(None)

        def add_textual(contents):
            le = gui.lineEdit(box, self, None,
                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                     QSizePolicy.Expanding))
            if contents:
                le.setText(contents)
            le.setAlignment(Qt.AlignRight)
            le.editingFinished.connect(self.conditions_changed)
            return le

        def add_numeric(contents):
            le = add_textual(contents)
            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())
            return le

        box = self.cond_list.cellWidget(oper_combo.row, 2)
        lc = ["", ""]
        oper = oper_combo.currentIndex()
        attr_name = oper_combo.attr_combo.currentText()
        if attr_name in self.AllTypes:
            vtype = self.AllTypes[attr_name]
            var = None
        else:
            var = self.data.domain[attr_name]
            var_idx = self.data.domain.index(attr_name)
            vtype = vartype(var)
            if selected_values is not None:
                lc = list(selected_values) + ["", ""]
                lc = [str(x) if vtype != 4 else x for x in lc[:2]]
        if box and vtype == box.var_type:
            lc = self._get_lineedit_contents(box) + lc

        if oper_combo.currentText().endswith(" defined"):
            label = QLabel()
            label.var_type = vtype
            self.cond_list.setCellWidget(oper_combo.row, 2, label)
        elif var is not None and var.is_discrete:
            if oper_combo.currentText().endswith(" one of"):
                if selected_values:
                    lc = list(selected_values)
                button = DropDownToolButton(self, var, lc)
                button.var_type = vtype
                self.cond_list.setCellWidget(oper_combo.row, 2, button)
            else:
                combo = ComboBoxSearch()
                combo.addItems(("", ) + var.values)
                if lc[0]:
                    combo.setCurrentIndex(int(lc[0]))
                else:
                    combo.setCurrentIndex(0)
                combo.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, combo)
                combo.currentIndexChanged.connect(self.conditions_changed)
        else:
            box = gui.hBox(self, addToLayout=False)
            box.var_type = vtype
            self.cond_list.setCellWidget(oper_combo.row, 2, box)
            if vtype == 2:  # continuous:
                box.controls = [add_numeric(lc[0])]
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_numeric(lc[1]))
            elif vtype == 3:  # string:
                box.controls = [add_textual(lc[0])]
                if oper in [6, 7]:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_textual(lc[1]))
            elif vtype == 4:  # time:
                def invalidate_datetime():
                    if w_:
                        if w.dateTime() > w_.dateTime():
                            w_.setDateTime(w.dateTime())
                        if w.format == (1, 1):
                            w.calendarWidget.timeedit.setTime(w.time())
                            w_.calendarWidget.timeedit.setTime(w_.time())
                    elif w.format == (1, 1):
                        w.calendarWidget.timeedit.setTime(w.time())

                def datetime_changed():
                    self.conditions_changed()
                    invalidate_datetime()

                datetime_format = (var.have_date, var.have_time)
                column = self.data[:, var_idx]
                w = DateTimeWidget(self, column, datetime_format)
                w.set_datetime(lc[0])
                box.controls = [w]
                box.layout().addWidget(w)
                w.dateTimeChanged.connect(datetime_changed)
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    w_ = DateTimeWidget(self, column, datetime_format)
                    w_.set_datetime(lc[1])
                    box.layout().addWidget(w_)
                    box.controls.append(w_)
                    invalidate_datetime()
                    w_.dateTimeChanged.connect(datetime_changed)
                else:
                    w_ = None
            else:
                box.controls = []
        if not adding_all:
            self.conditions_changed()

    @Inputs.data
    def set_data(self, data):
        self.closeContext()
        self.data = data
        self.cb_pa.setEnabled(not isinstance(data, SqlTable))
        self.cb_pc.setEnabled(not isinstance(data, SqlTable))
        self.remove_all_rows()
        self.add_button.setDisabled(data is None)
        self.add_all_button.setDisabled(
            data is None or
            len(data.domain.variables) + len(data.domain.metas) > 100)
        if not data:
            self.info.set_input_summary(self.info.NoInput)
            self.data_desc = None
            self.variable_model.set_domain(None)
            self.commit()
            return
        self.data_desc = report.describe_data_brief(data)
        self.variable_model.set_domain(data.domain)

        self.conditions = []
        self.openContext(data)
        for attr, cond_type, cond_value in self.conditions:
            if attr in self.variable_model:
                self.add_row(attr, cond_type, cond_value)
        if not self.cond_list.model().rowCount():
            self.add_row()

        self.info.set_input_summary(data.approx_len(),
                                    format_summary_details(data))
        self.unconditional_commit()

    def conditions_changed(self):
        try:
            cells_by_rows = (
                [self.cond_list.cellWidget(row, col) for col in range(3)]
                for row in range(self.cond_list.rowCount())
            )
            self.conditions = [
                (var_cell.currentData(gui.TableVariable) or var_cell.currentText(),
                 oper_cell.currentIndex(),
                 self._get_value_contents(val_cell))
                for var_cell, oper_cell, val_cell in cells_by_rows]
            if self.update_on_change and (
                    self.last_output_conditions is None or
                    self.last_output_conditions != self.conditions):
                self.commit()
        except AttributeError:
            # Attribute error appears if the signal is triggered when the
            # controls are being constructed
            pass

    @staticmethod
    def _values_to_floats(attr, values):
        if len(values) == 0:
            return values
        if not all(values):
            return None
        if isinstance(attr, TimeVariable):
            values = (value.toString(format=Qt.ISODate) for value in values)
            parse = lambda x: (attr.parse(x), True)
        else:
            parse = QLocale().toDouble

        try:
            floats, ok = zip(*[parse(v) for v in values])
            if not all(ok):
                raise ValueError('Some values could not be parsed as floats'
                                 'in the current locale: {}'.format(values))
        except TypeError:
            floats = values  # values already floats
        assert all(isinstance(v, float) for v in floats)
        return floats

    def commit(self):
        matching_output = self.data
        non_matching_output = None
        annotated_output = None

        self.Error.clear()
        if self.data:
            domain = self.data.domain
            conditions = []
            for attr_name, oper_idx, values in self.conditions:
                if attr_name in self.AllTypes:
                    attr_index = attr = None
                    attr_type = self.AllTypes[attr_name]
                    operators = self.Operators[attr_name]
                else:
                    attr_index = domain.index(attr_name)
                    attr = domain[attr_index]
                    attr_type = vartype(attr)
                    operators = self.Operators[type(attr)]
                opertype, _ = operators[oper_idx]
                if attr_type == 0:
                    filt = data_filter.IsDefined()
                elif attr_type in (2, 4):  # continuous, time
                    try:
                        floats = self._values_to_floats(attr, values)
                    except ValueError as e:
                        self.Error.parsing_error(e.args[0])
                        return
                    if floats is None:
                        continue
                    filt = data_filter.FilterContinuous(
                        attr_index, opertype, *floats)
                elif attr_type == 3:  # string
                    filt = data_filter.FilterString(
                        attr_index, opertype, *[str(v) for v in values])
                else:
                    if opertype == FilterDiscreteType.IsDefined:
                        f_values = None
                    else:
                        if not values or not values[0]:
                            continue
                        values = [attr.values[i-1] for i in values]
                        if opertype == FilterDiscreteType.Equal:
                            f_values = {values[0]}
                        elif opertype == FilterDiscreteType.NotEqual:
                            f_values = set(attr.values)
                            f_values.remove(values[0])
                        elif opertype == FilterDiscreteType.In:
                            f_values = set(values)
                        else:
                            raise ValueError("invalid operand")
                    filt = data_filter.FilterDiscrete(attr_index, f_values)
                conditions.append(filt)

            if conditions:
                filters = data_filter.Values(conditions)
                matching_output = filters(self.data)
                filters.negate = True
                non_matching_output = filters(self.data)

                row_sel = np.in1d(self.data.ids, matching_output.ids)
                annotated_output = create_annotated_table(self.data, row_sel)

            # if hasattr(self.data, "name"):
            #     matching_output.name = self.data.name
            #     non_matching_output.name = self.data.name

            purge_attrs = self.purge_attributes
            purge_classes = self.purge_classes
            if (purge_attrs or purge_classes) and \
                    not isinstance(self.data, SqlTable):
                attr_flags = sum([Remove.RemoveConstant * purge_attrs,
                                  Remove.RemoveUnusedValues * purge_attrs])
                class_flags = sum([Remove.RemoveConstant * purge_classes,
                                   Remove.RemoveUnusedValues * purge_classes])
                # same settings used for attributes and meta features
                remover = Remove(attr_flags, class_flags, attr_flags)

                matching_output = remover(matching_output)
                non_matching_output = remover(non_matching_output)
                annotated_output = remover(annotated_output)

        if not matching_output:
            matching_output = None
        if not non_matching_output:
            non_matching_output = None
        if not annotated_output:
            annotated_output = None

        self.Outputs.matching_data.send(matching_output)
        self.Outputs.unmatched_data.send(non_matching_output)
        self.Outputs.annotated_data.send(annotated_output)

        self.match_desc = report.describe_data_brief(matching_output)
        self.nonmatch_desc = report.describe_data_brief(non_matching_output)

        summary = matching_output.approx_len() if matching_output else \
            self.info.NoOutput
        details = format_summary_details(matching_output) if matching_output else ""
        self.info.set_output_summary(summary, details)

    def send_report(self):
        if not self.data:
            self.report_paragraph("No data.")
            return

        pdesc = None
        describe_domain = False
        for d in (self.data_desc, self.match_desc, self.nonmatch_desc):
            if not d or not d["Data instances"]:
                continue
            ndesc = d.copy()
            del ndesc["Data instances"]
            if pdesc is not None and pdesc != ndesc:
                describe_domain = True
            pdesc = ndesc

        conditions = []
        for attr, oper, values in self.conditions:
            if isinstance(attr, str):
                attr_name = attr
                var_type = self.AllTypes[attr]
                names = self.operator_names[attr_name]
            else:
                attr_name = attr.name
                var_type = vartype(attr)
                names = self.operator_names[type(attr)]
            name = names[oper]
            if oper == len(names) - 1:
                conditions.append("{} {}".format(attr_name, name))
            elif var_type == 1:  # discrete
                if name == "is one of":
                    valnames = [attr.values[v - 1] for v in values]
                    if not valnames:
                        continue
                    if len(valnames) == 1:
                        valstr = valnames[0]
                    else:
                        valstr = f"{', '.join(valnames[:-1])} or {valnames[-1]}"
                    conditions.append(f"{attr} is {valstr}")
                elif values and values[0]:
                    value = values[0] - 1
                    conditions.append(f"{attr} {name} {attr.values[value]}")
            elif var_type == 3:  # string variable
                conditions.append(
                    f"{attr} {name} {' and '.join(map(repr, values))}")
            elif var_type == 4:  # time
                values = (value.toString(format=Qt.ISODate) for value in values)
                conditions.append(f"{attr} {name} {' and '.join(values)}")
            elif all(x for x in values):  # numeric variable
                conditions.append(f"{attr} {name} {' and '.join(values)}")
        items = OrderedDict()
        if describe_domain:
            items.update(self.data_desc)
        else:
            items["Instances"] = self.data_desc["Data instances"]
        items["Condition"] = " AND ".join(conditions) or "no conditions"
        self.report_items("Data", items)
        if describe_domain:
            self.report_items("Matching data", self.match_desc)
            self.report_items("Non-matching data", self.nonmatch_desc)
        else:
            match_inst = \
                bool(self.match_desc) and \
                self.match_desc["Data instances"]
            nonmatch_inst = \
                bool(self.nonmatch_desc) and \
                self.nonmatch_desc["Data instances"]
            self.report_items(
                "Output",
                (("Matching data",
                  "{} instances".format(match_inst) if match_inst else "None"),
                 ("Non-matching data",
                  nonmatch_inst > 0 and "{} instances".format(nonmatch_inst))))

    # Uncomment this on 2022/2/2
    #
    # @classmethod
    # def migrate_context(cls, context, version):
    #     if not version or version < 2:
    #         # Just remove; can't migrate because variables types are unknown
    #         context.values["conditions"] = []


class CheckBoxPopup(QWidget):
    def __init__(self, var, lc, widget_parent=None, widg=None):
        QWidget.__init__(self)

        self.list_view = QListView()
        text = []
        model = QStandardItemModel(self.list_view)
        for (i, val) in enumerate(var.values):
            item = QStandardItem(val)
            item.setCheckable(True)
            if i + 1 in lc:
                item.setCheckState(Qt.Checked)
                text.append(val)
            model.appendRow(item)
        model.itemChanged.connect(widget_parent.conditions_changed)
        self.list_view.setModel(model)

        layout = QGridLayout(self)
        layout.addWidget(self.list_view)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.adjustSize()
        self.setWindowFlags(Qt.Popup)

        self.widget = widg
        self.widget.desc_text = ', '.join(text)
        self.widget.set_text()

    def moved(self):
        point = self.widget.rect().bottomRight()
        global_point = self.widget.mapToGlobal(point)
        self.move(global_point - QPoint(self.width(), 0))


class DropDownToolButton(QToolButton):
    def __init__(self, parent, var, lc):
        QToolButton.__init__(self, parent)
        self.desc_text = ''
        self.popup = CheckBoxPopup(var, lc, parent, self)
        self.setMenu(QMenu()) # to show arrow
        self.clicked.connect(self.open_popup)

    def open_popup(self):
        self.popup.moved()
        self.popup.show()

    def set_text(self):
        metrics = QFontMetrics(self.font())
        self.setText(metrics.elidedText(self.desc_text, Qt.ElideRight,
                                        self.width() - 15))

    def resizeEvent(self, _):
        self.set_text()


class DateTimeWidget(QDateTimeEdit):
    def __init__(self, parent, column, datetime_format):
        QDateTimeEdit.__init__(self, parent)

        self.format = datetime_format
        self.have_date, self.have_time = datetime_format[0], datetime_format[1]
        self.set_format(column)
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

    def set_format(self, column):
        str_format = Qt.ISODate
        if self.have_date and self.have_time:
            self.setDisplayFormat("yyyy-MM-dd hh:mm:ss")
            c_format = "%Y-%m-%d %H:%M:%S"
            min_datetime, max_datetime = self.find_range(column, c_format)
            self.min_datetime = QDateTime.fromString(min_datetime, str_format)
            self.max_datetime = QDateTime.fromString(max_datetime, str_format)
            self.setCalendarPopup(True)
            self.calendarWidget = CalendarWidgetWithTime(self)
            self.calendarWidget.timeedit.timeChanged.connect(
                self.set_datetime)
            self.setCalendarWidget(self.calendarWidget)
            self.setDateTimeRange(self.min_datetime, self.max_datetime)

        elif self.have_date and not self.have_time:
            self.setDisplayFormat("yyyy-MM-dd")
            self.setCalendarPopup(True)
            min_datetime, max_datetime = self.find_range(column, "%Y-%m-%d")
            self.min_datetime = QDate.fromString(min_datetime, str_format)
            self.max_datetime = QDate.fromString(max_datetime, str_format)
            self.setDateRange(self.min_datetime, self.max_datetime)

        elif not self.have_date and self.have_time:
            self.setDisplayFormat("hh:mm:ss")
            min_datetime, max_datetime = self.find_range(column, "%H:%M:%S")
            self.min_datetime = QTime.fromString(min_datetime, str_format)
            self.max_datetime = QTime.fromString(max_datetime, str_format)
            self.setTimeRange(self.min_datetime, self.max_datetime)

    def set_datetime(self, date_time):
        if not date_time:
            date_time = self.min_datetime
        if self.have_date and self.have_time:
            if isinstance(date_time, QTime):
                self.setDateTime(
                    QDateTime(self.date(), self.calendarWidget.timeedit.time()))
            else:
                self.setDateTime(date_time)
        elif self.have_date and not self.have_time:
            self.setDate(date_time)
        elif not self.have_date and self.have_time:
            self.setTime(date_time)

    @staticmethod
    def find_range(column, convert_format):
        def convert_timestamp(timestamp):
            if timestamp >= 0:
                return datetime.fromtimestamp(timestamp, tz=timezone.utc)
            return datetime(1970, 1, 1, tzinfo=timezone.utc) + \
                       timedelta(seconds=int(timestamp))

        item_list = [item for items in list(column) for item in items]
        min_datetime = convert_timestamp(
            np.nanmin(item_list)).strftime(convert_format)
        max_datetime = convert_timestamp(
            np.nanmax(item_list)).strftime(convert_format)
        return min_datetime, max_datetime


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWSelectRows).run(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