mathjson-solver 1.10.0__tar.gz → 1.12.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.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.2
2
+ Name: mathjson-solver
3
+ Version: 1.12.0
4
+ Summary: Utilities for MathJSON evaluation
5
+ Home-page: https://github.com/LongenesisLtd/mathjson-solver
6
+ Author: Martins Mednis
7
+ Author-email: mrt@mednis.info
8
+ Project-URL: Bug Tracker, https://github.com/LongenesisLtd/mathjson-solver/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: project-url
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # MathJSON Solver
26
+
27
+ [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
28
+ [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
29
+ ![Coverage](https://img.shields.io/badge/coverage-93-green)
30
+
31
+ _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
32
+
33
+ Please ask questions and share feedback in our Gitter chat [https://gitter.im/mathjson-solver/community](https://gitter.im/mathjson-solver/community).
34
+ [![Gitter](https://badges.gitter.im/mathjson-solver/community.svg)](https://gitter.im/mathjson-solver/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
35
+
36
+ ## How to use
37
+ ```python
38
+ from mathjson_solver import create_solver
39
+
40
+ parameters = {"x": 2, "y": 3}
41
+ expression = ["Add", "x", "y", 4]
42
+
43
+ solver = create_solver(parameters)
44
+ answer = solver(expression)
45
+
46
+ print(answer)
47
+ # 9, because 2+3+4=9
48
+ ```
49
+
50
+ ## Currently supported constructs
51
+ Find the full list of supported constructs in the [docs/README.md](https://github.com/LongenesisLtd/mathjson-solver/blob/main/docs/README.md).
52
+
53
+
54
+ ## Exception handling
55
+
56
+ A `MathJSONException` is raised when expression cannot be evaluated. Import `MathJSONException` to handle it:
57
+ ```python
58
+ from mathjson_solver import create_solver, MathJSONException
59
+
60
+ solver = create_solver({})
61
+ try:
62
+ solver(["Divide", 1, 0])
63
+ except MathJSONException:
64
+ pass
65
+ # invoke your own exception logger here
66
+ ```
67
+
68
+ Left unhandled, the exception will look like `MathJSONException("Problem in Divide. ['Divide', 1, 0]. division by zero")`.
69
+
70
+
71
+ ## How to run tests
72
+ Make sure you have `pytest` installed. Then `cd` into project directory and run:
73
+ ```bash
74
+ pytest
75
+ ```
76
+
77
+ ## Contributing Code
78
+
79
+ We welcome your contributions in the form of pull requests.
80
+
81
+ 1. Fork the repo;
82
+ 2. Make improvements;
83
+ 3. Make a pull request to share your improvements with the community and to include it into official release.
@@ -0,0 +1,59 @@
1
+ # MathJSON Solver
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
4
+ [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
5
+ ![Coverage](https://img.shields.io/badge/coverage-93-green)
6
+
7
+ _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
8
+
9
+ Please ask questions and share feedback in our Gitter chat [https://gitter.im/mathjson-solver/community](https://gitter.im/mathjson-solver/community).
10
+ [![Gitter](https://badges.gitter.im/mathjson-solver/community.svg)](https://gitter.im/mathjson-solver/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
11
+
12
+ ## How to use
13
+ ```python
14
+ from mathjson_solver import create_solver
15
+
16
+ parameters = {"x": 2, "y": 3}
17
+ expression = ["Add", "x", "y", 4]
18
+
19
+ solver = create_solver(parameters)
20
+ answer = solver(expression)
21
+
22
+ print(answer)
23
+ # 9, because 2+3+4=9
24
+ ```
25
+
26
+ ## Currently supported constructs
27
+ Find the full list of supported constructs in the [docs/README.md](https://github.com/LongenesisLtd/mathjson-solver/blob/main/docs/README.md).
28
+
29
+
30
+ ## Exception handling
31
+
32
+ A `MathJSONException` is raised when expression cannot be evaluated. Import `MathJSONException` to handle it:
33
+ ```python
34
+ from mathjson_solver import create_solver, MathJSONException
35
+
36
+ solver = create_solver({})
37
+ try:
38
+ solver(["Divide", 1, 0])
39
+ except MathJSONException:
40
+ pass
41
+ # invoke your own exception logger here
42
+ ```
43
+
44
+ Left unhandled, the exception will look like `MathJSONException("Problem in Divide. ['Divide', 1, 0]. division by zero")`.
45
+
46
+
47
+ ## How to run tests
48
+ Make sure you have `pytest` installed. Then `cd` into project directory and run:
49
+ ```bash
50
+ pytest
51
+ ```
52
+
53
+ ## Contributing Code
54
+
55
+ We welcome your contributions in the form of pull requests.
56
+
57
+ 1. Fork the repo;
58
+ 2. Make improvements;
59
+ 3. Make a pull request to share your improvements with the community and to include it into official release.
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = mathjson-solver
3
- version = 1.10.0
3
+ version = 1.12.0
4
4
  author = Martins Mednis
5
5
  author_email = mrt@mednis.info
6
6
  description = Utilities for MathJSON evaluation
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="mathjson-solver",
8
- version="1.10.0",
8
+ version="1.12.0",
9
9
  author="Martins Mednis",
10
10
  author_email="mrt@mednis.info",
11
11
  description="Utilities for MathJSON evaluation",
@@ -1,4 +1,5 @@
1
1
  import numbers
2
+ from types import NoneType
2
3
  from typing import Union
3
4
  from functools import reduce
4
5
  import math
@@ -26,17 +27,17 @@ class MathJSONException(Exception):
26
27
  return f"Problem in {self.construct}. {self.expr}. {m}"
27
28
 
28
29
 
29
- def requires_array(func):
30
- def inner1(*args, **kwargs):
31
- try:
32
- if args[0][1][0] == "Array" and len(args[0][1]) > 0:
33
- return func(*args, **kwargs)
34
- else:
35
- raise ValueError(f"'{func.__name__}' should receive a list")
36
- except TypeError:
37
- raise ValueError(f"'{func.__name__}' really should receive a list")
30
+ # def requires_array(func):
31
+ # def inner1(*args, **kwargs):
32
+ # try:
33
+ # if args[0][1][0] == "Array" and len(args[0][1]) > 0:
34
+ # return func(*args, **kwargs)
35
+ # else:
36
+ # raise ValueError(f"'{func.__name__}' should receive a list")
37
+ # except TypeError:
38
+ # raise ValueError(f"'{func.__name__}' really should receive a list")
38
39
 
39
- return inner1
40
+ # return inner1
40
41
 
41
42
 
42
43
  def is_numeric(x):
@@ -92,40 +93,51 @@ def has_matching_sublist(
92
93
  return count >= required_match_count
93
94
 
94
95
 
95
- def has_sublist2(
96
- *,
97
- my_list: list,
98
- required_match_count: int,
99
- position: int,
100
- contiguous: bool,
101
- condition: callable,
102
- ) -> bool:
103
- if contiguous:
104
- # Check for contiguous matches based on position
105
- if position == 0:
106
- # Check if the beginning of the list matches
107
- count = sum(1 for x in my_list[:required_match_count] if condition(x))
108
- return count == required_match_count
109
- elif position > 0:
110
- # Skip the first `position` elements
111
- count = sum(
112
- 1
113
- for x in my_list[position : position + required_match_count]
114
- if condition(x)
115
- )
116
- return count == required_match_count
117
- elif position == -1:
118
- # Check if the end of the list matches
119
- count = sum(1 for x in my_list[-required_match_count:] if condition(x))
120
- return count == required_match_count
121
- elif position < -1:
122
- # Skip the last `abs(position)` elements
123
- count = sum(1 for x in my_list[:position] if condition(x))
124
- return count == required_match_count
125
- else:
126
- # Check for non-contiguous matches
127
- count = sum(1 for x in my_list if condition(x))
128
- return count >= required_match_count
96
+ # def has_sublist2(
97
+ # *,
98
+ # my_list: list,
99
+ # required_match_count: int,
100
+ # position: int,
101
+ # contiguous: bool,
102
+ # condition: callable,
103
+ # ) -> bool:
104
+ # if contiguous:
105
+ # # Check for contiguous matches based on position
106
+ # if position == 0:
107
+ # # Check if the beginning of the list matches
108
+ # count = sum(1 for x in my_list[:required_match_count] if condition(x))
109
+ # return count == required_match_count
110
+ # elif position > 0:
111
+ # # Skip the first `position` elements
112
+ # count = sum(
113
+ # 1
114
+ # for x in my_list[position : position + required_match_count]
115
+ # if condition(x)
116
+ # )
117
+ # return count == required_match_count
118
+ # elif position == -1:
119
+ # # Check if the end of the list matches
120
+ # count = sum(1 for x in my_list[-required_match_count:] if condition(x))
121
+ # return count == required_match_count
122
+ # elif position < -1:
123
+ # # Skip the last `abs(position)` elements
124
+ # count = sum(1 for x in my_list[:position] if condition(x))
125
+ # return count == required_match_count
126
+ # else:
127
+ # # Check for non-contiguous matches
128
+ # count = sum(1 for x in my_list if condition(x))
129
+ # return count >= required_match_count
130
+
131
+
132
+ def comparison_safe_converter(x):
133
+ if type(x) in [int, float, str]:
134
+ return f"{x}"
135
+ elif type(x) in [bool, NoneType]:
136
+ if x:
137
+ return True
138
+ else:
139
+ return False
140
+ return x
129
141
 
130
142
 
131
143
  def create_mathjson_solver(solver_parameters):
@@ -147,7 +159,7 @@ def create_mathjson_solver(solver_parameters):
147
159
  tmp = 0
148
160
  for i, x in enumerate(s[1:]):
149
161
  res = f(x, c)
150
- if i ==0:
162
+ if i == 0:
151
163
  tmp = res
152
164
  else:
153
165
  tmp = tmp + res
@@ -177,14 +189,12 @@ def create_mathjson_solver(solver_parameters):
177
189
  l_res.append(res)
178
190
  return sum(l_res)
179
191
 
180
- # @requires_array
181
192
  def Max(s):
182
193
  if isinstance(s[1], str):
183
194
  return max([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
184
195
  else:
185
196
  return max([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
186
197
 
187
- # @requires_array
188
198
  def Min(s):
189
199
  if isinstance(s[1], str):
190
200
  return min([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
@@ -203,14 +213,12 @@ def create_mathjson_solver(solver_parameters):
203
213
  except ZeroDivisionError:
204
214
  return None
205
215
 
206
- # @requires_array
207
216
  def Median(s):
208
217
  if isinstance(s[1], str):
209
218
  return median([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
210
219
  else:
211
220
  return median([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
212
221
 
213
- # @requires_array
214
222
  def Length(s):
215
223
  if isinstance(s[1], str):
216
224
 
@@ -218,11 +226,9 @@ def create_mathjson_solver(solver_parameters):
218
226
  else:
219
227
  return len([x for x in s[1][1:]])
220
228
 
221
- # @requires_array
222
229
  def Any(s):
223
230
  return any([f(x, c) for x in s[1][1:]])
224
231
 
225
- # @requires_array
226
232
  def All(s):
227
233
  return all([f(x, c) for x in s[1][1:]])
228
234
 
@@ -397,6 +403,26 @@ def create_mathjson_solver(solver_parameters):
397
403
  def TimeDeltaWeeks(s):
398
404
  return datetime.timedelta(weeks=f(s[1], c))
399
405
 
406
+ # def Equal(s):
407
+ # a = comparison_safe_converter(f(s[1], c))
408
+ # b = comparison_safe_converter(f(s[2], c))
409
+ # return a == b
410
+ # if type(a) in [int, float, str]:
411
+ # a = f"{a}"
412
+ # elif type(a) in [bool, NoneType]:
413
+ # if a:
414
+ # a = True
415
+ # else:
416
+ # a = None
417
+
418
+ # if is_numeric(a):
419
+
420
+ # lambda s: f"{f(s[1], c)}" == f"{f(s[2], c)}",
421
+
422
+ def IsDefined(s):
423
+ # Todo: This does not work
424
+ return s[1] in c.keys()
425
+
400
426
  constructs = {
401
427
  "Sum": Sum,
402
428
  "Add": Add,
@@ -419,7 +445,10 @@ def create_mathjson_solver(solver_parameters):
419
445
  "Log": lambda s: math.log(f(s[1], c)),
420
446
  "Log2": lambda s: math.log2(f(s[1], c)),
421
447
  "Log10": lambda s: math.log10(f(s[1], c)),
422
- "Equal": lambda s: f(s[1], c) == f(s[2], c),
448
+ # "Equal": lambda s: f"{f(s[1], c)}" == f"{f(s[2], c)}",
449
+ "Equal": lambda s: comparison_safe_converter(f(s[1], c))
450
+ == comparison_safe_converter(f(s[2], c)),
451
+ "StrictEqual": lambda s: f(s[1], c) == f(s[2], c),
423
452
  "Greater": lambda s: f(s[1], c) > f(s[2], c),
424
453
  "GreaterEqual": lambda s: f(s[1], c) >= f(s[2], c),
425
454
  "Less": lambda s: f(s[1], c) < f(s[2], c),
@@ -452,7 +481,8 @@ def create_mathjson_solver(solver_parameters):
452
481
  "Float": Float,
453
482
  "Str": Str,
454
483
  "Not": Not,
455
- "IsDefined": lambda s: s[1] in c,
484
+ # "IsDefined": lambda s: s[1] in c,
485
+ "IsDefined": IsDefined,
456
486
  "Map": Map,
457
487
  "HasMatchingSublist": HasMatchingSublist,
458
488
  "Strptime": Strptime,
@@ -467,7 +497,17 @@ def create_mathjson_solver(solver_parameters):
467
497
  if s[0] in constructs:
468
498
  try:
469
499
  return constructs[s[0]](s)
470
- except Exception as e:
500
+
501
+ # except RecursionError:
502
+ # return s[0]
503
+ # except Exception as e:
504
+ except TypeError as e:
505
+ raise MathJSONException(e, s, mathjson_construct=s[0])
506
+ except ValueError as e:
507
+ raise MathJSONException(e, s, mathjson_construct=s[0])
508
+ except IndexError as e:
509
+ raise MathJSONException(e, s, mathjson_construct=s[0])
510
+ except ZeroDivisionError as e:
471
511
  raise MathJSONException(e, s, mathjson_construct=s[0])
472
512
  else:
473
513
  # raise MathJSONException(
@@ -475,7 +515,10 @@ def create_mathjson_solver(solver_parameters):
475
515
  # )
476
516
  return s
477
517
  elif s in solver_parameters:
478
- return f(solver_parameters[s], c)
518
+ try:
519
+ return f(solver_parameters[s], c)
520
+ except RecursionError:
521
+ return solver_parameters[s]
479
522
  elif s in c:
480
523
  return f(c[s], c)
481
524
  else:
@@ -536,6 +579,14 @@ def extract_variables(s: Union[list, int, float, str], li: set, ignore_list: set
536
579
  "IsDefined",
537
580
  "Map",
538
581
  "HasMatchingSublist",
582
+ "Strptime",
583
+ "Strftime",
584
+ "Today",
585
+ "Now",
586
+ "TimeDeltaWeeks",
587
+ "TimeDeltaHours",
588
+ "TimeDeltaMinutes",
589
+ "TimeDeltaDays",
539
590
  ]
540
591
  if isinstance(s, str):
541
592
  if s in ignore_list:
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.2
2
+ Name: mathjson-solver
3
+ Version: 1.12.0
4
+ Summary: Utilities for MathJSON evaluation
5
+ Home-page: https://github.com/LongenesisLtd/mathjson-solver
6
+ Author: Martins Mednis
7
+ Author-email: mrt@mednis.info
8
+ Project-URL: Bug Tracker, https://github.com/LongenesisLtd/mathjson-solver/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: project-url
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # MathJSON Solver
26
+
27
+ [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
28
+ [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
29
+ ![Coverage](https://img.shields.io/badge/coverage-93-green)
30
+
31
+ _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
32
+
33
+ Please ask questions and share feedback in our Gitter chat [https://gitter.im/mathjson-solver/community](https://gitter.im/mathjson-solver/community).
34
+ [![Gitter](https://badges.gitter.im/mathjson-solver/community.svg)](https://gitter.im/mathjson-solver/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
35
+
36
+ ## How to use
37
+ ```python
38
+ from mathjson_solver import create_solver
39
+
40
+ parameters = {"x": 2, "y": 3}
41
+ expression = ["Add", "x", "y", 4]
42
+
43
+ solver = create_solver(parameters)
44
+ answer = solver(expression)
45
+
46
+ print(answer)
47
+ # 9, because 2+3+4=9
48
+ ```
49
+
50
+ ## Currently supported constructs
51
+ Find the full list of supported constructs in the [docs/README.md](https://github.com/LongenesisLtd/mathjson-solver/blob/main/docs/README.md).
52
+
53
+
54
+ ## Exception handling
55
+
56
+ A `MathJSONException` is raised when expression cannot be evaluated. Import `MathJSONException` to handle it:
57
+ ```python
58
+ from mathjson_solver import create_solver, MathJSONException
59
+
60
+ solver = create_solver({})
61
+ try:
62
+ solver(["Divide", 1, 0])
63
+ except MathJSONException:
64
+ pass
65
+ # invoke your own exception logger here
66
+ ```
67
+
68
+ Left unhandled, the exception will look like `MathJSONException("Problem in Divide. ['Divide', 1, 0]. division by zero")`.
69
+
70
+
71
+ ## How to run tests
72
+ Make sure you have `pytest` installed. Then `cd` into project directory and run:
73
+ ```bash
74
+ pytest
75
+ ```
76
+
77
+ ## Contributing Code
78
+
79
+ We welcome your contributions in the form of pull requests.
80
+
81
+ 1. Fork the repo;
82
+ 2. Make improvements;
83
+ 3. Make a pull request to share your improvements with the community and to include it into official release.