Repository: biolab/orange3

Issue:
Feature Constructor: Handle missing values for StringVariables
<!-- 
Thanks for taking the time to report a bug!
If you're raising an issue about an add-on (i.e., installed via Options > Add-ons), raise an issue in the relevant add-on's issue tracker instead. See: https://github.com/biolab?q=orange3
To fix the bug, we need to be able to reproduce it. Please answer the following questions to the best of your ability.
-->


**What's wrong?**
<!-- Be specific, clear, and concise. Include screenshots if relevant. -->
<!-- If you're getting an error message, copy it, and enclose it with three backticks (```). -->
```
domain = Domain([], metas=[StringVariable("S1")])
data = Table.from_list(domain, [["A"], ["B"], [None]])
self.widget.setData(data)

editor = StringFeatureEditor()
editor.nameedit.setText("S2")
editor.expressionedit.setText("S1 + S1")
self.widget.addFeature(editor.editorData())
self.widget.apply()
output = self.get_output(self.widget.Outputs.data)
print(output)
```
The above code produces the following output:
```
[[] {A, AA},
 [] {B, BB},
 [] {?, ??}]
```

where the rightmost cell in the last row should be '?'.



Discussion:

Resolving patch:
diff --git a/Orange/widgets/data/owfeatureconstructor.py b/Orange/widgets/data/owfeatureconstructor.py
--- a/Orange/widgets/data/owfeatureconstructor.py
+++ b/Orange/widgets/data/owfeatureconstructor.py
@@ -1266,7 +1266,7 @@ def __call_instance(self, instance: Instance):
     def extract_column(self, table: Table, var: Variable):
         data, _ = table.get_column_view(var)
         if var.is_string:
-            return list(map(var.str_val, data))
+            return data
         elif var.is_discrete and not self.use_values:
             values = np.array([*var.values, None], dtype=object)
             idx = data.astype(int)


Test patch:
diff --git a/Orange/widgets/data/tests/test_owfeatureconstructor.py b/Orange/widgets/data/tests/test_owfeatureconstructor.py
--- a/Orange/widgets/data/tests/test_owfeatureconstructor.py
+++ b/Orange/widgets/data/tests/test_owfeatureconstructor.py
@@ -20,7 +20,7 @@
     DiscreteDescriptor, ContinuousDescriptor, StringDescriptor,
     construct_variables, OWFeatureConstructor,
     FeatureEditor, DiscreteFeatureEditor, FeatureConstructorHandler,
-    DateTimeDescriptor)
+    DateTimeDescriptor, StringFeatureEditor)
 
 from Orange.widgets.data.owfeatureconstructor import (
     freevars, validate_exp, FeatureFunc
@@ -405,6 +405,20 @@ def test_discrete_no_values(self):
         self.wait_until_finished(self.widget)
         self.assertTrue(self.widget.Error.more_values_needed.is_shown())
 
+    def test_missing_strings(self):
+        domain = Domain([], metas=[StringVariable("S1")])
+        data = Table.from_list(domain, [["A"], ["B"], [None]])
+        self.widget.setData(data)
+
+        editor = StringFeatureEditor()
+        editor.nameedit.setText("S2")
+        editor.expressionedit.setText("S1 + S1")
+        self.widget.addFeature(editor.editorData())
+        self.widget.apply()
+        output = self.get_output(self.widget.Outputs.data)
+        np.testing.assert_equal(output.metas,
+                                [["A", "AA"], ["B", "BB"], ["", ""]])
+
     @patch("Orange.widgets.data.owfeatureconstructor.QMessageBox")
     def test_fix_values(self, msgbox):
         w = self.widget