Skip to content

Commit bf4ed5e

Browse files
authored
Merge pull request #2146 from jerneju/gh-2143-editdomain-duplicate
[FIX] Edit Domain: Prevent duplicate variable names
2 parents 00d302e + 4190bd4 commit bf4ed5e

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

Orange/widgets/data/oweditdomain.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def variable_from_description(description, compute_value=None):
7070
module, type_name, name, kwargs, attrs = description
7171
try:
7272
constructor = get_qualified(module, type_name)
73-
except (ImportError, AttributeError) as ex:
73+
except (ImportError, AttributeError):
7474
raise ValueError("Invalid descriptor type '{}.{}"
7575
"".format(module, type_name))
7676

@@ -213,7 +213,7 @@ def set_data(self, var):
213213
def get_data(self):
214214
"""Retrieve the modified variable.
215215
"""
216-
name = str(self.name_edit.text())
216+
name = str(self.name_edit.text()).strip()
217217
labels = self.labels_model.get_dict()
218218

219219
# Is the variable actually changed.
@@ -226,12 +226,15 @@ def get_data(self):
226226

227227
return var
228228

229+
def is_legal(self):
230+
name = str(self.name_edit.text()).strip()
231+
return not len(name) == 0
232+
229233
def is_same(self):
230234
"""Is the current model state the same as the input.
231235
"""
232-
name = str(self.name_edit.text())
236+
name = str(self.name_edit.text()).strip()
233237
labels = self.labels_model.get_dict()
234-
235238
return (self.var is not None and name == self.var.name and
236239
labels == self.var.attributes)
237240

@@ -243,7 +246,7 @@ def clear(self):
243246
self.labels_model.set_dict({})
244247

245248
def maybe_commit(self):
246-
if not self.is_same():
249+
if not self.is_same() and self.is_legal():
247250
self.commit()
248251

249252
def commit(self):
@@ -317,7 +320,7 @@ def set_data(self, var):
317320
def get_data(self):
318321
"""Retrieve the modified variable
319322
"""
320-
name = str(self.name_edit.text())
323+
name = str(self.name_edit.text()).strip()
321324
labels = self.labels_model.get_dict()
322325
values = map(str, self.values_model)
323326

@@ -402,6 +405,10 @@ def __init__(self):
402405

403406
box.layout().addWidget(self.editor_stack)
404407

408+
self.Error.add_message(
409+
"duplicate_var_name",
410+
"A variable name is duplicated.")
411+
405412
@check_sql_input
406413
def set_data(self, data):
407414
"""Set input data set."""
@@ -514,7 +521,9 @@ def _on_variable_changed(self):
514521

515522
# Replace the variable in the 'Domain Features' view/model
516523
old_var = self.input_vars[self.selected_index]
517-
new_var = editor.get_data().copy(compute_value=Orange.preprocess.transformation.Identity(old_var))
524+
new_var = editor.get_data().copy(
525+
compute_value=Orange.preprocess.transformation.Identity(old_var)
526+
)
518527
self.domain_model[self.selected_index] = new_var
519528

520529

@@ -530,17 +539,21 @@ def _invalidate(self):
530539
def commit(self):
531540
"""Send the changed data to output."""
532541
new_data = None
542+
var_names = [vn.name for vn in self.domain_model]
543+
self.Error.duplicate_var_name.clear()
533544
if self.data is not None:
534-
input_domain = self.data.domain
535-
n_attrs = len(input_domain.attributes)
536-
n_vars = len(input_domain.variables)
537-
n_class_vars = len(input_domain.class_vars)
538-
all_new_vars = list(self.domain_model)
539-
attrs = all_new_vars[: n_attrs]
540-
class_vars = all_new_vars[n_attrs: n_attrs + n_class_vars]
541-
new_metas = all_new_vars[n_attrs + n_class_vars:]
542-
new_domain = Orange.data.Domain(attrs, class_vars, new_metas)
543-
new_data = self.data.from_table(new_domain, self.data)
545+
if len(var_names) == len(set(var_names)):
546+
input_domain = self.data.domain
547+
n_attrs = len(input_domain.attributes)
548+
n_class_vars = len(input_domain.class_vars)
549+
all_new_vars = list(self.domain_model)
550+
attrs = all_new_vars[: n_attrs]
551+
class_vars = all_new_vars[n_attrs: n_attrs + n_class_vars]
552+
new_metas = all_new_vars[n_attrs + n_class_vars:]
553+
new_domain = Orange.data.Domain(attrs, class_vars, new_metas)
554+
new_data = self.data.from_table(new_domain, self.data)
555+
else:
556+
self.Error.duplicate_var_name()
544557

545558
self.send("Data", new_data)
546559

Orange/widgets/data/tests/test_oweditdomain.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,35 @@ def test_list_attributes_remain_lists(self):
140140
t2 = self.get_output("Data")
141141
self.assertEqual(t2.domain["a"].attributes["list"], [1, 2, 4])
142142

143+
def test_duplicate_names(self):
144+
"""
145+
Tests if widget shows error when duplicate name is entered.
146+
And tests if widget sends None data when error is shown.
147+
GH-2143
148+
GH-2146
149+
"""
150+
table = Table("iris")
151+
self.send_signal("Data", table)
152+
self.assertFalse(self.widget.Error.duplicate_var_name.is_shown())
153+
154+
idx = self.widget.domain_view.model().index(0)
155+
self.widget.domain_view.setCurrentIndex(idx)
156+
editor = self.widget.editor_stack.findChild(ContinuousVariableEditor)
157+
158+
editor.name_edit.setText("iris")
159+
editor.commit()
160+
self.widget.commit()
161+
self.assertTrue(self.widget.Error.duplicate_var_name.is_shown())
162+
output = self.get_output("Data")
163+
self.assertIsNone(output)
164+
165+
editor.name_edit.setText("sepal height")
166+
editor.commit()
167+
self.widget.commit()
168+
self.assertFalse(self.widget.Error.duplicate_var_name.is_shown())
169+
output = self.get_output("Data")
170+
self.assertIsInstance(output, Table)
171+
143172

144173
class TestEditors(GuiTest):
145174
def test_variable_editor(self):

0 commit comments

Comments
 (0)