Skip to content

Commit 5e4bc54

Browse files
committed
Select Rows: Fix crash on changed variable type
1 parent 8605fac commit 5e4bc54

File tree

2 files changed

+56
-9
lines changed

2 files changed

+56
-9
lines changed

Orange/widgets/data/owselectrows.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,31 @@ def encode_setting(self, context, setting, value):
5050
if context.attributes.get(attr) == CONTINUOUS:
5151
if values and isinstance(values[0], str):
5252
values = [QLocale().toDouble(v)[0] for v in values]
53-
value[i] = (attr, op, values)
53+
value[i] = (attr, context.attributes[attr], op, values)
5454
return super().encode_setting(context, setting, value)
5555

5656
def decode_setting(self, setting, value, domain=None):
5757
value = super().decode_setting(setting, value, domain)
5858
if setting.name == 'conditions':
59-
for i, (attr, op, values) in enumerate(value):
59+
for i, (attr, _, op, values) in enumerate(value):
6060
var = attr in domain and domain[attr]
6161
if var and var.is_continuous and not isinstance(var, TimeVariable):
6262
value[i] = (attr, op,
6363
list([QLocale().toString(float(i), 'f')
6464
for i in values]))
6565
return value
6666

67+
def match(self, context, domain, attrs, metas):
68+
if (attrs, metas) == (context.attributes, context.metas):
69+
return self.PERFECT_MATCH
70+
71+
conditions = context.values["conditions"]
72+
all_vars = attrs
73+
all_vars.update(metas)
74+
if all(all_vars.get(name) == tpe for name, tpe, *_ in conditions):
75+
return 0.5
76+
return self.NO_MATCH
77+
6778

6879
class FilterDiscreteType(enum.Enum):
6980
Equal = "Equal"
@@ -105,6 +116,8 @@ class Outputs:
105116
purge_classes = Setting(False, schema_only=True)
106117
auto_commit = Setting(True)
107118

119+
settings_version = 1
120+
108121
Operators = {
109122
ContinuousVariable: [
110123
(FilterContinuous.Equal, "equals"),
@@ -692,6 +705,12 @@ def send_report(self):
692705
("Non-matching data",
693706
nonmatch_inst > 0 and "{} instances".format(nonmatch_inst))))
694707

708+
@classmethod
709+
def migrate_context(cls, context, version):
710+
if not version:
711+
# Just remove; can't migrate because variables types are unknown
712+
context.values["conditions"] = []
713+
695714

696715
class CheckBoxPopup(QWidget):
697716
def __init__(self, var, lc, widget_parent=None, widget=None):

Orange/widgets/data/tests/test_owselectrows.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import numpy as np
88

99
from Orange.data import (
10-
Table, ContinuousVariable, StringVariable, DiscreteVariable)
10+
Table, ContinuousVariable, StringVariable, DiscreteVariable, Domain)
1111
from Orange.widgets.data.owselectrows import (
1212
OWSelectRows, FilterDiscreteType, SelectRowsContextHandler)
1313
from Orange.widgets.tests.base import WidgetTest, datasets
@@ -16,6 +16,7 @@
1616
from Orange.widgets.tests.utils import simulate, override_locale
1717
from Orange.widgets.utils.annotated_data import ANNOTATED_DATA_FEATURE_NAME
1818
from Orange.tests import test_filename
19+
from orangewidget.settings import VERSION_KEY
1920

2021
CFValues = {
2122
FilterContinuous.Equal: ["5.4"],
@@ -132,22 +133,22 @@ def test_stores_settings_in_invariant_locale(self):
132133
context = self.widget.current_context
133134
self.send_signal(self.widget.Inputs.data, None)
134135
saved_condition = context.values["conditions"][0]
135-
self.assertEqual(saved_condition[2][0], 5.2)
136+
self.assertEqual(saved_condition[3][0], 5.2)
136137

137138
@override_locale(QLocale.C)
138139
def test_restores_continuous_filter_in_c_locale(self):
139140
iris = Table("iris")[:5]
140141
# Settings with string value
141142
self.widget = self.widget_with_context(
142-
iris.domain, [["sepal length", 2, ("5.2",)]])
143+
iris.domain, [["sepal length", 102, 2, ("5.2",)]])
143144
self.send_signal(self.widget.Inputs.data, iris)
144145

145146
values = self.widget.conditions[0][2]
146147
self.assertTrue(values[0].startswith("5.2"))
147148

148149
# Settings with float value
149150
self.widget = self.widget_with_context(
150-
iris.domain, [["sepal length", 2, (5.2,)]])
151+
iris.domain, [["sepal length", 102, 2, (5.2,)]])
151152
self.send_signal(self.widget.Inputs.data, iris)
152153

153154
values = self.widget.conditions[0][2]
@@ -158,15 +159,15 @@ def test_restores_continuous_filter_in_sl_SI_locale(self):
158159
iris = Table("iris")[:5]
159160
# Settings with string value
160161
self.widget = self.widget_with_context(
161-
iris.domain, [["sepal length", 2, ("5.2",)]])
162+
iris.domain, [["sepal length", 102, 2, ("5.2",)]])
162163
self.send_signal(self.widget.Inputs.data, iris)
163164

164165
values = self.widget.conditions[0][2]
165166
self.assertTrue(values[0].startswith("5,2"))
166167

167168
# Settings with float value
168169
self.widget = self.widget_with_context(
169-
iris.domain, [["sepal length", 2, (5.2,)]])
170+
iris.domain, [["sepal length", 102, 2, (5.2,)]])
170171
self.send_signal(self.widget.Inputs.data, iris)
171172

172173
values = self.widget.conditions[0][2]
@@ -242,10 +243,37 @@ def test_annotated_data(self):
242243
np.testing.assert_equal(annotations[:50], True)
243244
np.testing.assert_equal(annotations[50:], False)
244245

246+
def test_change_var_type(self):
247+
iris = Table("iris")
248+
domain = iris.domain
249+
250+
self.send_signal(self.widget.Inputs.data, iris)
251+
self.widget.remove_all_button.click()
252+
self.enterFilter(domain[0], "is below", "5.2")
253+
254+
var0vals = list({str(x) for x in iris.X[:, 0]})
255+
new_domain = Domain(
256+
(DiscreteVariable(domain[0].name, values=var0vals), )
257+
+ domain.attributes[1:],
258+
domain.class_var)
259+
new_iris = iris.transform(new_domain)
260+
self.send_signal(self.widget.Inputs.data, new_iris)
261+
262+
def test_migration_to_version_1(self):
263+
iris = Table("iris")
264+
265+
ch = SelectRowsContextHandler()
266+
context = ch.new_context(iris.domain, *ch.encode_domain(iris.domain))
267+
context.values = dict(conditions=[["petal length", 2, (5.2,)]])
268+
settings = dict(context_settings=[context])
269+
widget = self.create_widget(OWSelectRows, settings)
270+
self.assertEqual(widget.conditions, [])
271+
245272
def widget_with_context(self, domain, conditions):
246273
ch = SelectRowsContextHandler()
247274
context = ch.new_context(domain, *ch.encode_domain(domain))
248-
context.values = dict(conditions=conditions)
275+
context.values = {"conditions": conditions,
276+
VERSION_KEY: OWSelectRows.settings_version}
249277
settings = dict(context_settings=[context])
250278

251279
return self.create_widget(OWSelectRows, settings)

0 commit comments

Comments
 (0)