Skip to content

Commit

Permalink
fix: use homebrew jexl analyzer for question extraction
Browse files Browse the repository at this point in the history
Change analyzer baseclass to include array literals when walking AST.
  • Loading branch information
Stefan Borer committed Feb 12, 2021
1 parent 7620eec commit a81c089
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 8 deletions.
33 changes: 29 additions & 4 deletions caluma/caluma_core/jexl.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from logging import getLogger

import pyjexl
from pyjexl.analysis import ValidatingAnalyzer
from pyjexl.analysis import JEXLAnalyzer, ValidatingAnalyzer
from pyjexl.exceptions import ParseError
from pyjexl.parser import Literal
from pyjexl.parser import ArrayLiteral, Literal, ObjectLiteral
from rest_framework import exceptions

log = getLogger(__name__)
Expand Down Expand Up @@ -151,7 +151,32 @@ def evaluate(self, expression, context=None):
self._expr_stack.pop()


class ExtractTransformSubjectAnalyzer(ValidatingAnalyzer):
class CalumaAnalyzer(JEXLAnalyzer):
"""Analyzer visiting Object and ArrayLiterals.
TODO: Upstream this.
"""

def generic_visit(self, expression):
for child in expression.children:
assert child is not None
for c in self.visit(child):
yield c

if isinstance(expression, ArrayLiteral):
for child in expression.value:
assert child is not None
for c in self.visit(child):
yield c

elif isinstance(expression, ObjectLiteral):
for child in expression.value.values():
assert child is not None
for c in self.visit(child):
yield c


class ExtractTransformSubjectAnalyzer(CalumaAnalyzer):
"""
Extract all referenced subjects of given transforms.
Expand All @@ -171,7 +196,7 @@ def visit_Transform(self, transform):
yield from self.generic_visit(transform)


class ExtractTransformArgumentAnalyzer(ValidatingAnalyzer):
class ExtractTransformArgumentAnalyzer(CalumaAnalyzer):
"""
Extract all referenced arguments of given transforms.
Expand Down
20 changes: 19 additions & 1 deletion caluma/caluma_core/tests/test_jexl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pyjexl
import pytest

from ..jexl import JEXL, Cache, ExtractTransformSubjectAnalyzer
from ..jexl import JEXL, Cache, CalumaAnalyzer, ExtractTransformSubjectAnalyzer


@pytest.mark.parametrize(
Expand Down Expand Up @@ -189,3 +189,21 @@ def test_intersects_operator(expression, result):
)
def test_math_transforms(expression, result):
assert JEXL().evaluate(expression) == result


@pytest.mark.parametrize(
"expression,expected",
[
("1 + 1", [1, 1]),
('["1", 2, 3]', ["1", 2, 3]),
('{key: ["foo", "bar"]}', ["foo", "bar"]),
("[1|round, [2, 3]|min]|avg", [1, 2, 3]),
],
)
def test_caluma_analyzer(expression, expected):
class NodeAnalyzer(CalumaAnalyzer):
def visit_Literal(self, node):
yield node.value

jexl = JEXL()
assert list(jexl.analyze(expression, NodeAnalyzer)) == expected
4 changes: 2 additions & 2 deletions caluma/caluma_form/jexl.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ def validate(self, expression, **kwargs):
return super().validate(expression, QuestionValidatingAnalyzer)

def extract_referenced_questions(self, expr):
transforms = ["answer", "mapby"]
transforms = ["answer"]
yield from self.analyze(
expr, partial(ExtractTransformSubjectAnalyzer, transforms=transforms)
)

def extract_referenced_mapby_questions(self, expr):
transforms = ["answer", "mapby"]
transforms = ["mapby"]
yield from self.analyze(
expr, partial(ExtractTransformArgumentAnalyzer, transforms=transforms)
)
Expand Down
2 changes: 1 addition & 1 deletion caluma/caluma_form/tests/test_question.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ def test_calc_dependents(db, question_factory):

calc_q = question_factory(
type=models.Question.TYPE_CALCULATED_FLOAT,
calc_expression=f"'{dep1.slug}'|answer + '{dep2.slug}'|answer",
calc_expression=f"['{dep1.slug}'|answer, '{dep2.slug}'|answer]|sum",
)
other_calc_q = question_factory(
type=models.Question.TYPE_CALCULATED_FLOAT,
Expand Down

0 comments on commit a81c089

Please sign in to comment.