Skip to content

Commit

Permalink
Merge pull request #5231 from dimagi/named-filters-in-expressions
Browse files Browse the repository at this point in the history
support for named filters in expressions
  • Loading branch information
Nick Pellegrino committed Jan 7, 2015
2 parents 66eae63 + 335010f commit ffae6ce
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 13 deletions.
14 changes: 7 additions & 7 deletions corehq/apps/userreports/expressions/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@
ConditionalExpressionSpec, ConstantGetterSpec


def _simple_expression_generator(wrapper_class, spec):
def _simple_expression_generator(wrapper_class, spec, context):
return wrapper_class.wrap(spec).expression

_constant_expression = functools.partial(_simple_expression_generator, ConstantGetterSpec)
_property_name_expression = functools.partial(_simple_expression_generator, PropertyNameGetterSpec)
_property_path_expression = functools.partial(_simple_expression_generator, PropertyPathGetterSpec)


def _conditional_expression(spec):
def _conditional_expression(spec, context):
# no way around this since the two factories inherently depend on each other
from corehq.apps.userreports.filters.factory import FilterFactory
wrapped = ConditionalExpressionSpec.wrap(spec)
return ConditionalExpression(
FilterFactory.from_spec(wrapped.test),
ExpressionFactory.from_spec(wrapped.expression_if_true),
ExpressionFactory.from_spec(wrapped.expression_if_false),
FilterFactory.from_spec(wrapped.test, context),
ExpressionFactory.from_spec(wrapped.expression_if_true, context),
ExpressionFactory.from_spec(wrapped.expression_if_false, context),
)


Expand All @@ -36,9 +36,9 @@ class ExpressionFactory(object):
}

@classmethod
def from_spec(cls, spec):
def from_spec(cls, spec, context=None):
try:
return cls.spec_map[spec['type']](spec)
return cls.spec_map[spec['type']](spec, context)
except KeyError:
raise BadSpecError(_('Invalid getter type: {}. Valid options are: {}').format(
spec['type'],
Expand Down
2 changes: 1 addition & 1 deletion corehq/apps/userreports/filters/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _build_property_match_filter(spec, context):
def _build_boolean_expression_filter(spec, context):
wrapped = BooleanExpressionFilterSpec.wrap(spec)
return SinglePropertyValueFilter(
expression=ExpressionFactory.from_spec(wrapped.expression),
expression=ExpressionFactory.from_spec(wrapped.expression, context),
operator=get_operator(wrapped.operator),
reference_value=wrapped.property_value,
)
Expand Down
2 changes: 1 addition & 1 deletion corehq/apps/userreports/indicators/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _build_expression_indicator(spec, context):
return RawIndicator(
wrapped.display_name,
column,
getter=wrapped.parsed_expression,
getter=wrapped.parsed_expression(context),
)


Expand Down
5 changes: 2 additions & 3 deletions corehq/apps/userreports/indicators/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ class ExpressionIndicatorSpec(IndicatorSpecBase):
is_primary_key = BooleanProperty(default=False)
expression = DictProperty(required=True)

@property
def parsed_expression(self):
def parsed_expression(self, context):
transform = _transform_from_datatype(self.datatype)
expression = ExpressionFactory.from_spec(self.expression)
expression = ExpressionFactory.from_spec(self.expression, context)
return TransformedGetter(expression, transform)


Expand Down
77 changes: 76 additions & 1 deletion corehq/apps/userreports/tests/test_indicator_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ def setUp(self):
'type': 'property_match',
'property_name': 'mother_state',
'property_value': 'pregnant',
},
'evil': {
'type': 'property_match',
'property_name': 'evil',
'property_value': 'yes',
}
},
'configured_filter': {
Expand All @@ -158,7 +163,37 @@ def setUp(self):
'name': 'pregnant',
}
]
}
},
'configured_indicators': [
{
"type": "boolean",
"column_id": "is_evil",
"filter": {
"type": "named",
"name": "evil"
}
},
{
"type": "expression",
"column_id": "laugh_sound",
"datatype": "string",
"expression": {
'type': 'conditional',
'test': {
"type": "named",
"name": "evil"
},
'expression_if_true': {
'type': 'constant',
'constant': 'mwa-ha-ha',
},
'expression_if_false': {
'type': 'constant',
'constant': 'hehe',
},
}
}
]
})

def test_match(self):
Expand All @@ -176,3 +211,43 @@ def test_no_match(self):
'type': 'ttc_mother',
'mother_state': 'not pregnant'
}))

def test_simple_indicator_match(self):
values = self.indicator_configuration.indicators.get_values({
'doc_type': 'CommCareCase',
'domain': 'test',
'type': 'ttc_mother',
'mother_state': 'pregnant',
'evil': 'yes'
})
self.assertEqual(1, values[1].value)

def test_simple_indicator_nomatch(self):
values = self.indicator_configuration.indicators.get_values({
'doc_type': 'CommCareCase',
'domain': 'test',
'type': 'ttc_mother',
'mother_state': 'pregnant',
'evil': 'no'
})
self.assertEqual(0, values[1].value)

def test_expression_match(self):
values = self.indicator_configuration.indicators.get_values({
'doc_type': 'CommCareCase',
'domain': 'test',
'type': 'ttc_mother',
'mother_state': 'pregnant',
'evil': 'yes'
})
self.assertEqual('mwa-ha-ha', values[2].value)

def test_expression_nomatch(self):
values = self.indicator_configuration.indicators.get_values({
'doc_type': 'CommCareCase',
'domain': 'test',
'type': 'ttc_mother',
'mother_state': 'pregnant',
'evil': 'no'
})
self.assertEqual('hehe', values[2].value)

0 comments on commit ffae6ce

Please sign in to comment.