mathjson-solver 1.7.0__tar.gz → 1.8.0__tar.gz
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.
- {mathjson_solver-1.7.0/src/mathjson_solver.egg-info → mathjson_solver-1.8.0}/PKG-INFO +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/setup.cfg +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/setup.py +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver/__init__.py +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver/__main__.py +122 -33
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0/src/mathjson_solver.egg-info}/PKG-INFO +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/tests/test_with_pytest.py +30 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/LICENSE +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/README.md +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/pyproject.toml +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver.egg-info/SOURCES.txt +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver.egg-info/dependency_links.txt +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver.egg-info/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from .__main__ import create_mathjson_solver as create_solver
|
|
2
|
-
from .__main__ import MathJSONException
|
|
2
|
+
from .__main__ import MathJSONException, extract_variables
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import numbers
|
|
2
|
+
from typing import Union
|
|
2
3
|
from functools import reduce
|
|
3
4
|
import math
|
|
4
5
|
from copy import deepcopy
|
|
@@ -60,36 +61,52 @@ def create_mathjson_solver(solver_parameters):
|
|
|
60
61
|
def Arr(s):
|
|
61
62
|
return s
|
|
62
63
|
|
|
63
|
-
@requires_array
|
|
64
|
+
# @requires_array
|
|
64
65
|
def Max(s):
|
|
65
|
-
|
|
66
|
+
if isinstance(s[1], str):
|
|
67
|
+
return max([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
68
|
+
else:
|
|
69
|
+
return max([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
66
70
|
|
|
67
|
-
@requires_array
|
|
71
|
+
# @requires_array
|
|
68
72
|
def Min(s):
|
|
69
|
-
|
|
73
|
+
if isinstance(s[1], str):
|
|
74
|
+
return min([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
75
|
+
else:
|
|
76
|
+
return min([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
70
77
|
|
|
71
|
-
@requires_array
|
|
72
78
|
def Average(s):
|
|
73
|
-
|
|
79
|
+
if isinstance(s[1], str):
|
|
80
|
+
# A reference to "answer" has been passed
|
|
81
|
+
s_ = [float(f(x, c)) for x in f(s[1], c) if is_numeric(f(x, c))]
|
|
82
|
+
else:
|
|
83
|
+
s_ = [float(f(x, c)) for x in s[1][1:] if is_numeric(f(x, c))]
|
|
74
84
|
# print(f"{s_} {sum(s_)}/{len(s_)}")
|
|
75
85
|
try:
|
|
76
86
|
return sum(s_) / len(s_)
|
|
77
87
|
except ZeroDivisionError:
|
|
78
88
|
return None
|
|
79
89
|
|
|
80
|
-
@requires_array
|
|
90
|
+
# @requires_array
|
|
81
91
|
def Median(s):
|
|
82
|
-
|
|
92
|
+
if isinstance(s[1], str):
|
|
93
|
+
return median([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
94
|
+
else:
|
|
95
|
+
return median([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
83
96
|
|
|
84
|
-
@requires_array
|
|
97
|
+
# @requires_array
|
|
85
98
|
def Length(s):
|
|
86
|
-
|
|
99
|
+
if isinstance(s[1], str):
|
|
100
|
+
|
|
101
|
+
return len([x for x in f(s[1], c)][1:])
|
|
102
|
+
else:
|
|
103
|
+
return len([x for x in s[1][1:]])
|
|
87
104
|
|
|
88
|
-
@requires_array
|
|
105
|
+
# @requires_array
|
|
89
106
|
def Any(s):
|
|
90
107
|
return any([f(x, c) for x in s[1][1:]])
|
|
91
108
|
|
|
92
|
-
@requires_array
|
|
109
|
+
# @requires_array
|
|
93
110
|
def All(s):
|
|
94
111
|
return all([f(x, c) for x in s[1][1:]])
|
|
95
112
|
|
|
@@ -112,7 +129,7 @@ def create_mathjson_solver(solver_parameters):
|
|
|
112
129
|
for x in s[3:]:
|
|
113
130
|
if len(x) != 2:
|
|
114
131
|
raise ValueError(
|
|
115
|
-
|
|
132
|
+
"Case of 'Switch' should have exactly two parameters"
|
|
116
133
|
)
|
|
117
134
|
if expression == f(x[0], c):
|
|
118
135
|
return f(x[1], c)
|
|
@@ -121,20 +138,20 @@ def create_mathjson_solver(solver_parameters):
|
|
|
121
138
|
|
|
122
139
|
def If(s):
|
|
123
140
|
if len(s) < 3:
|
|
124
|
-
raise ValueError(
|
|
141
|
+
raise ValueError("Wrong parameters for 'If'")
|
|
125
142
|
for x in s[1:-1]:
|
|
126
143
|
if len(x) != 2:
|
|
127
|
-
raise ValueError(
|
|
144
|
+
raise ValueError("Wrong if or elif in 'If'")
|
|
128
145
|
try:
|
|
129
146
|
if f(x[0], c):
|
|
130
147
|
try:
|
|
131
148
|
return f(x[1], c)
|
|
132
|
-
except MathJSONException
|
|
149
|
+
except MathJSONException:
|
|
133
150
|
logging.error(
|
|
134
151
|
"MathJSONException: %s", traceback.format_exc()
|
|
135
152
|
)
|
|
136
153
|
continue
|
|
137
|
-
except MathJSONException
|
|
154
|
+
except MathJSONException:
|
|
138
155
|
logging.error("MathJSONException: %s", traceback.format_exc())
|
|
139
156
|
return f(s[-1], c) # return default value (else)
|
|
140
157
|
|
|
@@ -142,29 +159,29 @@ def create_mathjson_solver(solver_parameters):
|
|
|
142
159
|
|
|
143
160
|
def In(s):
|
|
144
161
|
if len(s) != 3:
|
|
145
|
-
raise ValueError(
|
|
146
|
-
if
|
|
162
|
+
raise ValueError("Wrong parameters for 'In'")
|
|
163
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
147
164
|
return f(s[1], c) in [f(x, c) for x in s[2][1:]]
|
|
148
165
|
|
|
149
|
-
elif
|
|
166
|
+
elif isinstance(s[2], str):
|
|
150
167
|
return f(s[1], c) in f(s[2], c)
|
|
151
168
|
else:
|
|
152
169
|
raise ValueError(
|
|
153
|
-
|
|
170
|
+
"Wrong parameters for 'In'. Parameter 2 must be a list."
|
|
154
171
|
)
|
|
155
172
|
|
|
156
173
|
def Not_in(s):
|
|
157
174
|
return not In(s)
|
|
158
175
|
|
|
159
176
|
def Contains_any_of(s):
|
|
160
|
-
if
|
|
177
|
+
if isinstance(s[1], list) and s[1][0] == "Array":
|
|
161
178
|
list1 = [f(x, c) for x in s[1][1:]]
|
|
162
|
-
elif
|
|
179
|
+
elif isinstance(s[1], str):
|
|
163
180
|
list1 = f(s[1], c)
|
|
164
181
|
|
|
165
|
-
if
|
|
182
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
166
183
|
list2 = [f(x, c) for x in s[2][1:]]
|
|
167
|
-
elif
|
|
184
|
+
elif isinstance(s[2], str):
|
|
168
185
|
list2 = f(s[2], c)
|
|
169
186
|
|
|
170
187
|
if any(x in list1 for x in list2):
|
|
@@ -172,14 +189,14 @@ def create_mathjson_solver(solver_parameters):
|
|
|
172
189
|
return False
|
|
173
190
|
|
|
174
191
|
def Contains_all_of(s):
|
|
175
|
-
if
|
|
192
|
+
if isinstance(s[1], list) and s[1][0] == "Array":
|
|
176
193
|
list1 = [f(x, c) for x in s[1][1:]]
|
|
177
|
-
elif
|
|
194
|
+
elif isinstance(s[1], str):
|
|
178
195
|
list1 = f(s[1], c)
|
|
179
196
|
|
|
180
|
-
if
|
|
197
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
181
198
|
list2 = [f(x, c) for x in s[2][1:]]
|
|
182
|
-
elif
|
|
199
|
+
elif isinstance(s[2], str):
|
|
183
200
|
list2 = f(s[2], c)
|
|
184
201
|
|
|
185
202
|
if all(x in list1 for x in list2):
|
|
@@ -191,7 +208,7 @@ def create_mathjson_solver(solver_parameters):
|
|
|
191
208
|
|
|
192
209
|
def Str(s):
|
|
193
210
|
if len(s) < 2:
|
|
194
|
-
raise ValueError(
|
|
211
|
+
raise ValueError("Wrong parameters for 'Str'")
|
|
195
212
|
return f"{f(s[1])}"
|
|
196
213
|
|
|
197
214
|
def Not(s):
|
|
@@ -226,9 +243,11 @@ def create_mathjson_solver(solver_parameters):
|
|
|
226
243
|
"LessEqual": lambda s: f(s[1], c) <= f(s[2], c),
|
|
227
244
|
"NotEqual": lambda s: f(s[1], c) != f(s[2], c),
|
|
228
245
|
"Abs": lambda s: abs(f(s[1], c)),
|
|
229
|
-
"Round": lambda s:
|
|
230
|
-
|
|
231
|
-
|
|
246
|
+
"Round": lambda s: (
|
|
247
|
+
round(f(s[1], c), f(s[2], c))
|
|
248
|
+
if len(s) == 3
|
|
249
|
+
else int(round(f(s[1], c)))
|
|
250
|
+
),
|
|
232
251
|
"Max": Max,
|
|
233
252
|
"Min": Min,
|
|
234
253
|
"Average": Average,
|
|
@@ -271,3 +290,73 @@ def create_mathjson_solver(solver_parameters):
|
|
|
271
290
|
return s
|
|
272
291
|
|
|
273
292
|
return f
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def extract_variables(s: Union[list, int, float, str], li: set, ignore_list: set):
|
|
296
|
+
constructs = [
|
|
297
|
+
"Add",
|
|
298
|
+
"Sum",
|
|
299
|
+
"Subtract",
|
|
300
|
+
"Constants",
|
|
301
|
+
"Switch",
|
|
302
|
+
"If",
|
|
303
|
+
"Multiply",
|
|
304
|
+
"Divide",
|
|
305
|
+
"Negate",
|
|
306
|
+
"Power",
|
|
307
|
+
"Root",
|
|
308
|
+
"Sqrt",
|
|
309
|
+
"Square",
|
|
310
|
+
"Exp",
|
|
311
|
+
"Log",
|
|
312
|
+
"Log2",
|
|
313
|
+
"Log10",
|
|
314
|
+
"Equal",
|
|
315
|
+
"Greater",
|
|
316
|
+
"GreaterEqual",
|
|
317
|
+
"Less",
|
|
318
|
+
"LessEqual",
|
|
319
|
+
"NotEqual",
|
|
320
|
+
"Abs",
|
|
321
|
+
"Round",
|
|
322
|
+
"Max",
|
|
323
|
+
"Min",
|
|
324
|
+
"Average",
|
|
325
|
+
"Median",
|
|
326
|
+
"Length",
|
|
327
|
+
"Any",
|
|
328
|
+
"All",
|
|
329
|
+
"Array",
|
|
330
|
+
"In",
|
|
331
|
+
"Not_in",
|
|
332
|
+
"Contains_any_of",
|
|
333
|
+
"Contains_all_of",
|
|
334
|
+
"Contains_none_of",
|
|
335
|
+
"NotIn",
|
|
336
|
+
"ContainsAnyOf",
|
|
337
|
+
"ContainsAllOf",
|
|
338
|
+
"ContainsNoneOf",
|
|
339
|
+
"Int",
|
|
340
|
+
"Float",
|
|
341
|
+
"Str",
|
|
342
|
+
"Not",
|
|
343
|
+
"IsDefined",
|
|
344
|
+
]
|
|
345
|
+
if isinstance(s, str):
|
|
346
|
+
if s in ignore_list:
|
|
347
|
+
return li
|
|
348
|
+
if s not in constructs:
|
|
349
|
+
li.add(s)
|
|
350
|
+
return li
|
|
351
|
+
elif isinstance(s, list):
|
|
352
|
+
if s[0] == "Constants":
|
|
353
|
+
for x in s[1:-1]:
|
|
354
|
+
ignore_list.add(x[0])
|
|
355
|
+
li.update(extract_variables(x[1], li, ignore_list))
|
|
356
|
+
li.update(extract_variables(x[-1], li, ignore_list))
|
|
357
|
+
else:
|
|
358
|
+
for x in s[1:]:
|
|
359
|
+
li.update(extract_variables(x, li, ignore_list))
|
|
360
|
+
return li
|
|
361
|
+
else:
|
|
362
|
+
return li
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
|
|
6
6
|
sys.path.append(os.path.join(os.path.dirname(__file__), "../src/"))
|
|
7
7
|
|
|
8
|
-
from mathjson_solver import create_solver, MathJSONException
|
|
8
|
+
from mathjson_solver import create_solver, MathJSONException, extract_variables
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@pytest.mark.parametrize(
|
|
@@ -52,7 +52,11 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
52
52
|
({}, ["Array", 1, 2, 3, 5, 2], ["Array", 1, 2, 3, 5, 2]),
|
|
53
53
|
({}, ["Max", ["Array", 1, 2, 3, 5, 2]], 5),
|
|
54
54
|
({}, ["Max", ["Array", 1, 2, ["Sum", 2, 4, 3], 5, 2]], 9),
|
|
55
|
+
({"a": ["Array", 1, 2, 3, 5, 2]}, ["Max", "a"], 5),
|
|
56
|
+
({}, ["Min", ["Array", 1, 2, 3, 5, 2]], 1),
|
|
57
|
+
({"a": ["Array", 2, 1, 3, 5, 2]}, ["Min", "a"], 1),
|
|
55
58
|
({}, ["Median", ["Array", 1, 2, 3, 5, 2]], 2),
|
|
59
|
+
({"a": ["Array", 1, 2, 3, 5, 2]}, ["Median", "a"], 2),
|
|
56
60
|
({}, ["Average", ["Array", 1, 2, 3, 5, 2]], 2.6),
|
|
57
61
|
(
|
|
58
62
|
{},
|
|
@@ -80,8 +84,10 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
80
84
|
5.0,
|
|
81
85
|
),
|
|
82
86
|
({}, ["Average", ["Array"]], None),
|
|
87
|
+
({"a": ["Array", 2, 8]}, ["Average", "a"], 5),
|
|
83
88
|
({"a": 10, "b": 20}, ["Average", ["Array", "a", "b"]], 15),
|
|
84
89
|
({}, ["Length", ["Array", 1, 2, 3, 5, 2, 9]], 6),
|
|
90
|
+
({"a": ["Array", 1, 2, 3, 5, 2, 9]}, ["Length", "a"], 6),
|
|
85
91
|
({}, ["Length", ["Array"]], 0),
|
|
86
92
|
({}, ["Int", "12"], 12),
|
|
87
93
|
({}, ["Int", "12.2"], 12),
|
|
@@ -206,3 +212,26 @@ def test_handle_exception():
|
|
|
206
212
|
assert True
|
|
207
213
|
else:
|
|
208
214
|
assert False
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.mark.parametrize(
|
|
218
|
+
"parameters, expression, expected_result",
|
|
219
|
+
[
|
|
220
|
+
({}, ["Add", 2, 4, 3], set()),
|
|
221
|
+
({}, ["Sum", 2, "a", 3], set(["a"])),
|
|
222
|
+
({"a": 5}, ["Sum", 2, "a", 3], set()),
|
|
223
|
+
({"a": 5, "b": 6}, ["Sum", 2, "a", "c"], set(["c"])),
|
|
224
|
+
({}, ["Constants", ["c", 1], ["d", ["Add", 2, "a"]], "d"], set(["a"])),
|
|
225
|
+
# Now with "ugly" variables that mimic the ones used for deep referencing.
|
|
226
|
+
(
|
|
227
|
+
{},
|
|
228
|
+
["Add", "[slug1][0][question1]", "y", 4, "[slug1][-3:-1][question1]"],
|
|
229
|
+
set(["[slug1][0][question1]", "y", "[slug1][-3:-1][question1]"]),
|
|
230
|
+
),
|
|
231
|
+
],
|
|
232
|
+
)
|
|
233
|
+
def test_extract_variables(parameters, expression, expected_result):
|
|
234
|
+
assert (
|
|
235
|
+
extract_variables(expression, set(), set([x for x in parameters.keys()]))
|
|
236
|
+
== expected_result
|
|
237
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mathjson_solver-1.7.0 → mathjson_solver-1.8.0}/src/mathjson_solver.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|