lf-pollywog 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/conf.py +39 -0
- lf_pollywog-0.1.1.dist-info/METADATA +166 -0
- lf_pollywog-0.1.1.dist-info/RECORD +22 -0
- lf_pollywog-0.1.1.dist-info/WHEEL +5 -0
- lf_pollywog-0.1.1.dist-info/licenses/LICENSE +21 -0
- lf_pollywog-0.1.1.dist-info/top_level.txt +3 -0
- pollywog/__init__.py +2 -0
- pollywog/conversion/__init__.py +0 -0
- pollywog/conversion/sklearn.py +154 -0
- pollywog/core.py +838 -0
- pollywog/display.py +149 -0
- pollywog/helpers.py +246 -0
- pollywog/run.py +116 -0
- pollywog/utils.py +95 -0
- tests/__init__.py +0 -0
- tests/conftest.py +4 -0
- tests/test_conversion.py +72 -0
- tests/test_core.py +274 -0
- tests/test_display.py +60 -0
- tests/test_helpers.py +72 -0
- tests/test_run.py +71 -0
- tests/test_utils.py +32 -0
tests/test_core.py
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
def test_query_with_external_variable():
|
2
|
+
from pollywog.core import CalcSet, Variable, Number
|
3
|
+
|
4
|
+
a = Variable(name="a", children=["foo"])
|
5
|
+
b = Number(name="b1", children=["[a] + 1"])
|
6
|
+
c = Number(name="b2", children=["[b1] + 2"])
|
7
|
+
cs = CalcSet([a, b, c])
|
8
|
+
prefix = "b"
|
9
|
+
# Should select items whose name starts with prefix
|
10
|
+
result = cs.query("name.startswith(@prefix)")
|
11
|
+
names = [item.name for item in result.items]
|
12
|
+
assert set(names) == {"b1", "b2"}
|
13
|
+
|
14
|
+
|
15
|
+
def test_query_with_multiple_external_vars():
|
16
|
+
from pollywog.core import CalcSet, Variable, Number
|
17
|
+
|
18
|
+
a = Variable(name="a", children=["foo"])
|
19
|
+
b = Number(name="b1", children=["[a] + 1"])
|
20
|
+
c = Number(name="b2", children=["[b1] + 2"])
|
21
|
+
cs = CalcSet([a, b, c])
|
22
|
+
prefix = "b"
|
23
|
+
suffix = "2"
|
24
|
+
# Should select items whose name starts with prefix and ends with suffix
|
25
|
+
result = cs.query("name.startswith(@prefix) and name.endswith(@suffix)")
|
26
|
+
names = [item.name for item in result.items]
|
27
|
+
assert names == ["b2"]
|
28
|
+
|
29
|
+
|
30
|
+
def test_topological_sort_simple():
|
31
|
+
from pollywog.core import Number, Variable, CalcSet
|
32
|
+
|
33
|
+
a = Variable(name="a", children=["foo"])
|
34
|
+
b = Number(name="b", children=["[a] + 1"])
|
35
|
+
c = Number(name="c", children=["[b] + 2"])
|
36
|
+
cs = CalcSet([c, b, a])
|
37
|
+
sorted_cs = cs.topological_sort()
|
38
|
+
names = [item.name for item in sorted_cs.items]
|
39
|
+
assert names == ["a", "b", "c"]
|
40
|
+
|
41
|
+
|
42
|
+
def test_topological_sort_external_dep():
|
43
|
+
from pollywog.core import Number, Variable, CalcSet
|
44
|
+
|
45
|
+
a = Variable(name="a", children=["foo"])
|
46
|
+
b = Number(name="b", children=["[external] + 1"])
|
47
|
+
cs = CalcSet([b, a])
|
48
|
+
sorted_cs = cs.topological_sort()
|
49
|
+
names = [item.name for item in sorted_cs.items]
|
50
|
+
assert set(names) == {"a", "b"}
|
51
|
+
|
52
|
+
|
53
|
+
def test_topological_sort_cycle():
|
54
|
+
from pollywog.core import Number, CalcSet
|
55
|
+
|
56
|
+
a = Number(name="a", children=["[b] + 1"])
|
57
|
+
b = Number(name="b", children=["[a] + 2"])
|
58
|
+
cs = CalcSet([a, b])
|
59
|
+
import pytest
|
60
|
+
|
61
|
+
with pytest.raises(ValueError):
|
62
|
+
cs.topological_sort()
|
63
|
+
|
64
|
+
|
65
|
+
def test_item_rename():
|
66
|
+
num = Number(name="n1", children=["[x] + 1"])
|
67
|
+
# Rename item name only
|
68
|
+
num2 = num.rename(name="n2")
|
69
|
+
assert num2.name == "n2"
|
70
|
+
assert num2.children == ["[x] + 1"]
|
71
|
+
# Rename variable inside children
|
72
|
+
num3 = num.rename(variables={"x": "y"})
|
73
|
+
assert num3.children == ["[y] + 1"]
|
74
|
+
# Rename both name and variable
|
75
|
+
num4 = num.rename(name="n3", variables={"x": "z"})
|
76
|
+
assert num4.name == "n3"
|
77
|
+
assert num4.children == ["[z] + 1"]
|
78
|
+
|
79
|
+
|
80
|
+
def test_calcset_rename_items_and_variables():
|
81
|
+
num = Number(name="n1", children=["[x] + 1"])
|
82
|
+
var = Variable(name="x", children=["foo"])
|
83
|
+
filt = Filter(name="f1", children=["[x] > 0"])
|
84
|
+
cs = CalcSet([num, var, filt])
|
85
|
+
# Rename item names
|
86
|
+
cs2 = cs.rename(items={"n1": "n2", "f1": "f2"})
|
87
|
+
assert cs2.items[0].name == "n2"
|
88
|
+
assert cs2.items[2].name == "f2"
|
89
|
+
# Rename variable references in children
|
90
|
+
cs3 = cs.rename(variables={"x": "y"})
|
91
|
+
assert cs3.items[0].children == ["[y] + 1"]
|
92
|
+
assert cs3.items[2].children == ["[y] > 0"]
|
93
|
+
# Rename both items and variables
|
94
|
+
cs4 = cs.rename(items={"n1": "n3"}, variables={"x": "z"})
|
95
|
+
assert cs4.items[0].name == "n3"
|
96
|
+
assert cs4.items[0].children == ["[z] + 1"]
|
97
|
+
assert cs4.items[2].children == ["[z] > 0"]
|
98
|
+
|
99
|
+
|
100
|
+
def test_rename_with_regex():
|
101
|
+
num = Number(name="prefix_n1", children=["[var_x] + 1"])
|
102
|
+
var = Variable(name="var_x", children=["foo"])
|
103
|
+
cs = CalcSet([num, var])
|
104
|
+
# Rename with regex
|
105
|
+
cs2 = cs.rename(
|
106
|
+
items={r"^prefix_": "renamed_"}, variables={r"^var_": "newvar_"}, regex=True
|
107
|
+
)
|
108
|
+
assert cs2.items[0].name == "renamed_n1"
|
109
|
+
assert cs2.items[0].children == ["[newvar_x] + 1"]
|
110
|
+
assert cs2.items[1].name == "newvar_x"
|
111
|
+
|
112
|
+
|
113
|
+
def test_rename_nested_if():
|
114
|
+
ifrow = IfRow(condition=["[x] > 0"], value=["[x] + 1"])
|
115
|
+
ifexpr = If(rows=[ifrow], otherwise=["[x] - 1"])
|
116
|
+
num = Number(name="n1", children=[ifexpr])
|
117
|
+
cs = CalcSet([num])
|
118
|
+
cs2 = cs.rename(variables={"x": "y"})
|
119
|
+
nested_if = cs2.items[0].children[0]
|
120
|
+
assert isinstance(nested_if, If)
|
121
|
+
assert nested_if.rows[0].condition == ["[y] > 0"]
|
122
|
+
assert nested_if.rows[0].value == ["[y] + 1"]
|
123
|
+
assert nested_if.otherwise == ["[y] - 1"]
|
124
|
+
|
125
|
+
|
126
|
+
import pytest
|
127
|
+
from pollywog.core import CalcSet, Number
|
128
|
+
|
129
|
+
from pollywog.core import Variable, Filter, If, IfRow, Category
|
130
|
+
|
131
|
+
|
132
|
+
def test_number_to_dict_and_from_dict():
|
133
|
+
num = Number(name="n1", children=["1+2"])
|
134
|
+
d = num.to_dict()
|
135
|
+
num2 = Number.from_dict(d)
|
136
|
+
assert num2.name == "n1"
|
137
|
+
assert num2.children == ["1+2"]
|
138
|
+
|
139
|
+
|
140
|
+
def test_variable_and_filter():
|
141
|
+
var = Variable(name="v1", children=["foo"])
|
142
|
+
filt = Filter(name="f1", children=["bar"])
|
143
|
+
assert var.to_dict()["type"] == "variable"
|
144
|
+
assert filt.to_dict()["type"] == "filter"
|
145
|
+
|
146
|
+
|
147
|
+
def test_category():
|
148
|
+
cat = Category(name="cat1", children=["'A'"])
|
149
|
+
d = cat.to_dict()
|
150
|
+
assert d["calculation_type"] == "string"
|
151
|
+
|
152
|
+
|
153
|
+
def test_ifrow_and_if():
|
154
|
+
ifrow = IfRow(condition=["[x] > 0"], value=["1"])
|
155
|
+
d = ifrow.to_dict()
|
156
|
+
ifrow2 = IfRow.from_dict(d)
|
157
|
+
assert ifrow2.condition == ["[x] > 0"]
|
158
|
+
assert ifrow2.value == ["1"]
|
159
|
+
|
160
|
+
ifexpr = If(rows=[ifrow], otherwise=["0"])
|
161
|
+
d2 = ifexpr.to_dict()
|
162
|
+
ifexpr2 = If.from_dict(d2)
|
163
|
+
assert isinstance(ifexpr2, If)
|
164
|
+
assert isinstance(ifexpr2.rows[0], IfRow)
|
165
|
+
assert ifexpr2.otherwise == ["0"]
|
166
|
+
|
167
|
+
|
168
|
+
def test_calcset_serialization():
|
169
|
+
num = Number(name="n1", children=["1+2"])
|
170
|
+
var = Variable(name="v1", children=["foo"])
|
171
|
+
cs = CalcSet([num, var])
|
172
|
+
json_str = cs.to_json()
|
173
|
+
cs2 = CalcSet.from_dict(cs.to_dict())
|
174
|
+
assert isinstance(cs2, CalcSet)
|
175
|
+
assert len(cs2.items) == 2
|
176
|
+
|
177
|
+
|
178
|
+
def test_calcset_repr():
|
179
|
+
num = Number(name="n1", children=["1+2"])
|
180
|
+
cs = CalcSet([num])
|
181
|
+
s = repr(cs)
|
182
|
+
assert s.startswith("{")
|
183
|
+
|
184
|
+
|
185
|
+
def test_calcset_add_multiple():
|
186
|
+
num1 = Number(name="a", children=["2"])
|
187
|
+
num2 = Number(name="b", children=["3"])
|
188
|
+
var = Variable(name="v", children=["foo"])
|
189
|
+
cs1 = CalcSet([num1])
|
190
|
+
cs2 = CalcSet([num2, var])
|
191
|
+
cs3 = cs1 + cs2
|
192
|
+
assert len(cs3.items) == 3
|
193
|
+
assert cs3.items[2].name == "v"
|
194
|
+
|
195
|
+
|
196
|
+
def test_copy_independence():
|
197
|
+
num = Number(name="n1", children=["1+2"])
|
198
|
+
num_copy = num.copy()
|
199
|
+
assert isinstance(num_copy, Number)
|
200
|
+
assert num_copy.name == num.name
|
201
|
+
assert num_copy.children == num.children
|
202
|
+
num_copy.name = "n2"
|
203
|
+
num_copy.children[0] = "3+4"
|
204
|
+
assert num.name == "n1"
|
205
|
+
assert num.children[0] == "1+2"
|
206
|
+
|
207
|
+
var = Variable(name="v1", children=["foo"])
|
208
|
+
var_copy = var.copy()
|
209
|
+
var_copy.name = "v2"
|
210
|
+
assert var.name == "v1"
|
211
|
+
|
212
|
+
filt = Filter(name="f1", children=["bar"])
|
213
|
+
filt_copy = filt.copy()
|
214
|
+
filt_copy.name = "f2"
|
215
|
+
assert filt.name == "f1"
|
216
|
+
|
217
|
+
cat = Category(name="cat1", children=["'A'"])
|
218
|
+
cat_copy = cat.copy()
|
219
|
+
cat_copy.name = "cat2"
|
220
|
+
assert cat.name == "cat1"
|
221
|
+
|
222
|
+
ifrow = IfRow(condition=["[x] > 0"], value=["1"])
|
223
|
+
ifrow_copy = ifrow.copy()
|
224
|
+
ifrow_copy.condition[0] = "[x] < 0"
|
225
|
+
assert ifrow.condition[0] == "[x] > 0"
|
226
|
+
|
227
|
+
ifexpr = If(rows=[ifrow], otherwise=["0"])
|
228
|
+
ifexpr_copy = ifexpr.copy()
|
229
|
+
ifexpr_copy.rows[0].condition[0] = "[x] == 0"
|
230
|
+
assert ifexpr.rows[0].condition[0] == "[x] > 0"
|
231
|
+
|
232
|
+
cs = CalcSet([num, var, filt, cat, ifrow, ifexpr])
|
233
|
+
cs_copy = cs.copy()
|
234
|
+
cs_copy.items[0].name = "changed"
|
235
|
+
assert cs.items[0].name == "n1"
|
236
|
+
|
237
|
+
|
238
|
+
def test_error_handling():
|
239
|
+
# Wrong type for CalcSet.from_dict
|
240
|
+
with pytest.raises(ValueError):
|
241
|
+
CalcSet.from_dict({"type": "not-calcset", "items": []})
|
242
|
+
# Unknown item type
|
243
|
+
with pytest.raises(ValueError):
|
244
|
+
CalcSet.from_dict({"type": "calculation-set", "items": [{"type": "unknown"}]})
|
245
|
+
|
246
|
+
|
247
|
+
def test_ifrow_invalid_type():
|
248
|
+
with pytest.raises(ValueError):
|
249
|
+
IfRow.from_dict({"type": "not_if_row"})
|
250
|
+
|
251
|
+
|
252
|
+
def test_if_invalid_type():
|
253
|
+
with pytest.raises(ValueError):
|
254
|
+
If.from_dict({"type": "not_if"})
|
255
|
+
|
256
|
+
|
257
|
+
def test_calcset_to_dict():
|
258
|
+
num = Number(name="test_num", children=["1+1"])
|
259
|
+
calcset = CalcSet([num])
|
260
|
+
d = calcset.to_dict()
|
261
|
+
assert d["type"] == "calculation-set"
|
262
|
+
assert isinstance(d["items"], list)
|
263
|
+
assert d["items"][0]["name"] == "test_num"
|
264
|
+
|
265
|
+
|
266
|
+
def test_calcset_add():
|
267
|
+
num1 = Number(name="a", children=["2"])
|
268
|
+
num2 = Number(name="b", children=["3"])
|
269
|
+
cs1 = CalcSet([num1])
|
270
|
+
cs2 = CalcSet([num2])
|
271
|
+
cs3 = cs1 + cs2
|
272
|
+
assert len(cs3.items) == 2
|
273
|
+
assert cs3.items[0].name == "a"
|
274
|
+
assert cs3.items[1].name == "b"
|
tests/test_display.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
import pollywog.display
|
2
|
+
import pytest
|
3
|
+
|
4
|
+
# Dummy CalcSet and Item for testing
|
5
|
+
class DummyItem:
|
6
|
+
def __init__(self, name, typ, calc_type=None):
|
7
|
+
self.name = name
|
8
|
+
self.item_type = typ
|
9
|
+
self.calculation_type = calc_type
|
10
|
+
def to_dict(self):
|
11
|
+
d = {"name": self.name, "type": self.item_type}
|
12
|
+
if self.calculation_type:
|
13
|
+
d["calculation_type"] = self.calculation_type
|
14
|
+
return d
|
15
|
+
|
16
|
+
class DummyCalcSet:
|
17
|
+
def __init__(self, items):
|
18
|
+
self.items = items
|
19
|
+
|
20
|
+
# Test set_theme and theme switching
|
21
|
+
def test_set_theme():
|
22
|
+
pollywog.display.set_theme("dark")
|
23
|
+
assert pollywog.display._DISPLAY_THEME == "dark"
|
24
|
+
pollywog.display.set_theme("light")
|
25
|
+
assert pollywog.display._DISPLAY_THEME == "light"
|
26
|
+
with pytest.raises(ValueError):
|
27
|
+
pollywog.display.set_theme("unknown")
|
28
|
+
|
29
|
+
# Test display_calcset does not error and produces HTML
|
30
|
+
@pytest.mark.parametrize("theme", ["light", "dark"])
|
31
|
+
def test_display_calcset_html(theme):
|
32
|
+
pollywog.display.set_theme(theme)
|
33
|
+
items = [
|
34
|
+
DummyItem("A", "variable"),
|
35
|
+
DummyItem("B", "calculation", "number"),
|
36
|
+
DummyItem("C", "calculation", "string"),
|
37
|
+
DummyItem("D", "filter"),
|
38
|
+
]
|
39
|
+
calcset = DummyCalcSet(items)
|
40
|
+
# Should not raise
|
41
|
+
pollywog.display.display_calcset(calcset)
|
42
|
+
# Should produce HTML output (not None)
|
43
|
+
# (We can't check the actual rendering, but can check no error)
|
44
|
+
|
45
|
+
# Optionally, test that the HTML contains expected labels
|
46
|
+
@pytest.mark.parametrize("theme,label", [("light", "number"), ("dark", "string")])
|
47
|
+
def test_display_calcset_label(theme, label):
|
48
|
+
pollywog.display.set_theme(theme)
|
49
|
+
items = [DummyItem("B", "calculation", label)]
|
50
|
+
calcset = DummyCalcSet(items)
|
51
|
+
# Monkeypatch display to capture HTML
|
52
|
+
import IPython.display
|
53
|
+
captured = {}
|
54
|
+
def fake_display(obj):
|
55
|
+
captured["html"] = obj.data if hasattr(obj, "data") else str(obj)
|
56
|
+
orig_display = IPython.display.display
|
57
|
+
IPython.display.display = fake_display
|
58
|
+
pollywog.display.display_calcset(calcset)
|
59
|
+
IPython.display.display = orig_display
|
60
|
+
assert label in captured["html"]
|
tests/test_helpers.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
from pollywog.helpers import Average, Sum, Product, Normalize, WeightedAverage, Scale, IfElse, CategoryFromThresholds
|
2
|
+
from pollywog.core import Number
|
3
|
+
|
4
|
+
def test_average_helper():
|
5
|
+
n = Average("Au", "Ag", name="avg_Au_Ag")
|
6
|
+
assert isinstance(n, Number)
|
7
|
+
assert n.name == "avg_Au_Ag"
|
8
|
+
assert "/ 2" in n.children[0]
|
9
|
+
assert "[Au]" in n.children[0] and "[Ag]" in n.children[0]
|
10
|
+
|
11
|
+
def test_sum_helper():
|
12
|
+
n = Sum("Au", "Ag", name="sum_Au_Ag")
|
13
|
+
assert isinstance(n, Number)
|
14
|
+
assert n.name == "sum_Au_Ag"
|
15
|
+
assert "[Au]" in n.children[0] and "[Ag]" in n.children[0]
|
16
|
+
assert "+" in n.children[0]
|
17
|
+
|
18
|
+
def test_product_helper():
|
19
|
+
n = Product("Au", "Ag", name="prod_Au_Ag")
|
20
|
+
assert isinstance(n, Number)
|
21
|
+
assert n.name == "prod_Au_Ag"
|
22
|
+
assert "[Au]" in n.children[0] and "[Ag]" in n.children[0]
|
23
|
+
assert "*" in n.children[0]
|
24
|
+
|
25
|
+
def test_normalize_helper():
|
26
|
+
n = Normalize("Au", 0, 10, name="norm_Au")
|
27
|
+
assert isinstance(n, Number)
|
28
|
+
assert n.name == "norm_Au"
|
29
|
+
assert "[Au]" in n.children[0]
|
30
|
+
assert "/ (10 - 0)" in n.children[0]
|
31
|
+
|
32
|
+
def test_weighted_average_helper():
|
33
|
+
# Test with constant weights
|
34
|
+
n = WeightedAverage(["Au", "Ag"], [0.7, 0.3], name="wavg_Au_Ag")
|
35
|
+
assert isinstance(n, Number)
|
36
|
+
assert n.name == "wavg_Au_Ag"
|
37
|
+
assert "[Au] * 0.7" in n.children[0]
|
38
|
+
assert "[Ag] * 0.3" in n.children[0]
|
39
|
+
assert "/ (0.7 + 0.3)" in n.children[0] or "/ (1.0)" in n.children[0]
|
40
|
+
|
41
|
+
# Test with variable weights
|
42
|
+
n2 = WeightedAverage(["Au", "Ag"], ["w1", "w2"], name="wavg_Au_Ag_varw")
|
43
|
+
assert isinstance(n2, Number)
|
44
|
+
assert n2.name == "wavg_Au_Ag_varw"
|
45
|
+
assert "[Au] * [w1]" in n2.children[0]
|
46
|
+
assert "[Ag] * [w2]" in n2.children[0]
|
47
|
+
assert "/ ([w1] + [w2])" in n2.children[0]
|
48
|
+
|
49
|
+
def test_scale_helper():
|
50
|
+
n = Scale("Au", 2, name="Au_scaled")
|
51
|
+
assert isinstance(n, Number)
|
52
|
+
assert n.name == "Au_scaled"
|
53
|
+
assert "[Au] * 2" in n.children[0]
|
54
|
+
|
55
|
+
n2 = Scale("Ag", "factor", name="Ag_scaled")
|
56
|
+
assert isinstance(n2, Number)
|
57
|
+
assert n2.name == "Ag_scaled"
|
58
|
+
assert "[Ag] * [factor]" in n2.children[0]
|
59
|
+
|
60
|
+
def test_ifelse_helper():
|
61
|
+
n = IfElse("[Au] > 1", 2, 0, name="Au_ifelse")
|
62
|
+
assert n.name == "Au_ifelse"
|
63
|
+
# Should contain If block
|
64
|
+
assert hasattr(n, "children")
|
65
|
+
assert "If [Au] > 1 then 2 else 0" in n.comment_equation
|
66
|
+
|
67
|
+
def test_category_from_thresholds_helper():
|
68
|
+
n = CategoryFromThresholds("Au", [0.5, 1.0], ["Low", "Medium", "High"], name="Au_class")
|
69
|
+
assert n.name == "Au_class"
|
70
|
+
# Should contain If block
|
71
|
+
assert hasattr(n, "children")
|
72
|
+
assert "Classify Au by thresholds [0.5, 1.0]" in n.comment_equation
|
tests/test_run.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
import pytest
|
2
|
+
from pollywog.core import CalcSet, Variable, Number, If, IfRow
|
3
|
+
from pollywog.run import run_calcset
|
4
|
+
|
5
|
+
|
6
|
+
def test_import_run():
|
7
|
+
# Just test that run.py imports without error
|
8
|
+
assert True
|
9
|
+
|
10
|
+
|
11
|
+
def test_run_calcset_with_dict():
|
12
|
+
a = Variable(name="a", children=[""])
|
13
|
+
b = Number(name="b", children=["[a] + 1"])
|
14
|
+
c = Number(name="c", children=["[b] * 2"])
|
15
|
+
cs = CalcSet([a, b, c])
|
16
|
+
inputs = {"a": 3}
|
17
|
+
results = run_calcset(cs, inputs=inputs)
|
18
|
+
assert "a" not in results # Variable should not be in output by default
|
19
|
+
assert results["b"] == 4
|
20
|
+
assert results["c"] == 8
|
21
|
+
# Debug mode: output_variables=True
|
22
|
+
debug_results = run_calcset(cs, inputs=inputs, output_variables=True)
|
23
|
+
assert debug_results["a"] == 3
|
24
|
+
assert debug_results["b"] == 4
|
25
|
+
assert debug_results["c"] == 8
|
26
|
+
|
27
|
+
|
28
|
+
def test_run_calcset_with_if():
|
29
|
+
a = Variable(name="a", children=[""])
|
30
|
+
ifrow1 = IfRow(condition=["[a] > 0"], value=["1"])
|
31
|
+
ifrow2 = IfRow(condition=["[a] <= 0"], value=["-1"])
|
32
|
+
ifexpr = If(rows=[ifrow1, ifrow2], otherwise=["0"])
|
33
|
+
b = Number(name="b", children=[ifexpr])
|
34
|
+
cs = CalcSet([a, b])
|
35
|
+
results = run_calcset(cs, inputs={"a": 2})
|
36
|
+
assert "a" not in results
|
37
|
+
assert results["b"] == 1
|
38
|
+
results = run_calcset(cs, inputs={"a": -2})
|
39
|
+
assert results["b"] == -1
|
40
|
+
results = run_calcset(cs, inputs={"a": 0})
|
41
|
+
assert results["b"] == -1
|
42
|
+
# Debug mode
|
43
|
+
debug_results = run_calcset(cs, inputs={"a": 2}, output_variables=True)
|
44
|
+
assert debug_results["a"] == 2
|
45
|
+
assert debug_results["b"] == 1
|
46
|
+
|
47
|
+
|
48
|
+
def test_run_calcset_with_dataframe():
|
49
|
+
import pandas as pd
|
50
|
+
|
51
|
+
a = Variable(name="a", children=[""])
|
52
|
+
b = Number(name="b", children=["[a] + 1"])
|
53
|
+
cs = CalcSet([a, b])
|
54
|
+
df = pd.DataFrame({"a": [1, 2, 3]})
|
55
|
+
result_df = run_calcset(cs, dataframe=df)
|
56
|
+
assert list(result_df["b"]) == [2, 3, 4]
|
57
|
+
assert "a" not in result_df.columns # Variable column should be dropped
|
58
|
+
# Debug mode
|
59
|
+
debug_df = run_calcset(cs, dataframe=df, output_variables=True)
|
60
|
+
assert "a" in debug_df.columns
|
61
|
+
assert list(debug_df["a"]) == [1, 2, 3]
|
62
|
+
|
63
|
+
|
64
|
+
def test_pw_accessor():
|
65
|
+
import pandas as pd
|
66
|
+
|
67
|
+
b = Number(name="b", children=["[a] + 1"])
|
68
|
+
cs = CalcSet([b])
|
69
|
+
df = pd.DataFrame({"a": [10, 20]})
|
70
|
+
result_df = df.pw.run(cs)
|
71
|
+
assert list(result_df["b"]) == [11, 21]
|
tests/test_utils.py
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
from pollywog.utils import ensure_list, ensure_str_list, to_dict
|
2
|
+
|
3
|
+
|
4
|
+
class Dummy:
|
5
|
+
def to_dict(self):
|
6
|
+
return {"dummy": True}
|
7
|
+
|
8
|
+
|
9
|
+
def test_ensure_list():
|
10
|
+
assert ensure_list(1) == [1]
|
11
|
+
assert ensure_list([1, 2]) == [1, 2]
|
12
|
+
|
13
|
+
|
14
|
+
def test_ensure_str_list():
|
15
|
+
assert ensure_str_list("foo") == ["foo"]
|
16
|
+
assert ensure_str_list(["foo"]) == ["foo"]
|
17
|
+
assert ensure_str_list([1]) == ["", 1, ""]
|
18
|
+
assert ensure_str_list(["foo", 1]) == ["foo", 1, ""]
|
19
|
+
assert ensure_str_list(["foo", "bar"]) == ["foo", "bar"]
|
20
|
+
|
21
|
+
|
22
|
+
def test_to_dict():
|
23
|
+
d = Dummy()
|
24
|
+
assert to_dict(d) == [{"dummy": True}]
|
25
|
+
assert to_dict([d, d]) == [{"dummy": True}, {"dummy": True}]
|
26
|
+
assert to_dict(["a", "b"]) == ["a", "b"]
|
27
|
+
assert to_dict(["a", d]) == ["a", {"dummy": True}]
|
28
|
+
# guard_strings True
|
29
|
+
assert to_dict([d], guard_strings=True) == ["", {"dummy": True}, ""]
|
30
|
+
assert to_dict(["a", d], guard_strings=True) == ["a", {"dummy": True}, ""]
|
31
|
+
assert to_dict([d, "a"], guard_strings=True) == ["", {"dummy": True}, "a"]
|
32
|
+
assert to_dict(["a", "b"], guard_strings=True) == ["a", "b"]
|