Beispiel #1
0
class RelatedDocExpressionSpec(JsonObject):
    type = TypeProperty('related_doc')
    related_doc_type = StringProperty()
    doc_id_expression = DictProperty(required=True)
    value_expression = DictProperty(required=True)

    db_lookup = lambda self, type: get_db()

    def configure(self, related_doc_type, doc_id_expression, value_expression):
        self._related_doc_type = related_doc_type
        self._doc_id_expression = doc_id_expression
        self._value_expression = value_expression

        # used in caching
        self._vary_on = json.dumps(self.value_expression, sort_keys=True)

    def __call__(self, item, context=None):
        doc_id = self._doc_id_expression(item, context)
        if doc_id:
            return self.get_value(doc_id, context)

    @quickcache(['self._vary_on', 'doc_id'])
    def get_value(self, doc_id, context):
        try:
            doc = self.db_lookup(self.related_doc_type).get(doc_id)
            # ensure no cross-domain lookups of different documents
            assert context.root_doc['domain']
            if context.root_doc['domain'] != doc.get('domain'):
                return None
            # explicitly use a new evaluation context since this is a new document
            return self._value_expression(doc, EvaluationContext(doc, 0))
        except ResourceNotFound:
            return None
class CaseTypeMeta(JsonObject):
    name = StringProperty(required=True)
    relationships = DictProperty()  # relationship name -> case type
    properties = ListProperty(CaseProperty)  # property -> CaseProperty
    opened_by = DictProperty(ConditionList)  # form_ids -> [FormActionCondition, ...]
    closed_by = DictProperty(ConditionList)  # form_ids -> [FormActionCondition, ...]
    error = StringProperty()
    has_errors = BooleanProperty()

    def get_property(self, name, allow_parent=False):
        if not allow_parent:
            assert '/' not in name, "Add parent properties to the correct case type"
        try:
            prop = next(prop for prop in self.properties if prop.name == name)
        except StopIteration:
            prop = CaseProperty(name=name)
            self.properties.append(prop)
        return prop

    def add_opener(self, form_id, condition):
        openers = self.opened_by.get(form_id, ConditionList())
        if condition.type == 'if':
            # only add optional conditions
            openers.conditions.append(condition)
        self.opened_by[form_id] = openers

    def add_closer(self, form_id, condition):
        closers = self.closed_by.get(form_id, ConditionList())
        if condition.type == 'if':
            # only add optional conditions
            closers.conditions.append(condition)
        self.closed_by[form_id] = closers
Beispiel #3
0
class ResumableIteratorState(JsonObject):
    doc_type = "ResumableIteratorState"
    _id = StringProperty()
    name = StringProperty()
    timestamp = DateTimeProperty()
    args = ListProperty()
    kwargs = DictProperty()
    retry = DictProperty()
    progress = DictProperty()
    complete = BooleanProperty(default=False)
Beispiel #4
0
class ResumableIteratorState(JsonObject):
    doc_type = "ResumableIteratorState"
    _id = StringProperty()
    name = StringProperty()
    timestamp = DateTimeProperty()
    args = ListProperty()
    kwargs = DictProperty()
    progress = DictProperty()

    def is_resume(self):
        return bool(getattr(self, '_rev', None))
Beispiel #5
0
class NestedExpressionSpec(JsonObject):
    type = TypeProperty('nested')
    argument_expression = DictProperty(required=True)
    value_expression = DictProperty(required=True)

    def configure(self, argument_expression, value_expression):
        self._argument_expression = argument_expression
        self._value_expression = value_expression

    def __call__(self, item, context=None):
        argument = self._argument_expression(item, context)
        return self._value_expression(argument, context)
Beispiel #6
0
class DynamicChoiceListFilterSpec(FilterSpec):
    type = TypeProperty('dynamic_choice_list')
    show_all = BooleanProperty(default=True)
    datatype = DataTypeProperty(default='string')
    choice_provider = DictProperty()
    ancestor_expression = DictProperty(default={}, required=False)

    def get_choice_provider_spec(self):
        return self.choice_provider or {'type': DATA_SOURCE_COLUMN}

    @property
    def choices(self):
        return []
Beispiel #7
0
class RelatedDocExpressionSpec(JsonObject):
    type = TypeProperty('related_doc')
    related_doc_type = StringProperty()
    doc_id_expression = DictProperty(required=True)
    value_expression = DictProperty(required=True)

    def configure(self, doc_id_expression, value_expression):
        non_couch_doc_types = (LOCATION_DOC_TYPE, )
        if (self.related_doc_type not in non_couch_doc_types
                and get_db_by_doc_type(self.related_doc_type) is None):
            raise BadSpecError(
                'Cannot determine database for document type {}!'.format(
                    self.related_doc_type))

        self._doc_id_expression = doc_id_expression
        self._value_expression = value_expression

    def __call__(self, item, context=None):
        doc_id = self._doc_id_expression(item, context)
        if doc_id:
            return self.get_value(doc_id, context)

    @staticmethod
    @ucr_context_cache(vary_on=(
        'related_doc_type',
        'doc_id',
    ))
    def _get_document(related_doc_type, doc_id, context):
        document_store = get_document_store_for_doc_type(
            context.root_doc['domain'],
            related_doc_type,
            load_source="related_doc_expression")
        try:
            doc = document_store.get_document(doc_id)
        except DocumentNotFoundError:
            return None
        if context.root_doc['domain'] != doc.get('domain'):
            return None
        return doc

    def get_value(self, doc_id, context):
        assert context.root_doc['domain']
        doc = self._get_document(self.related_doc_type, doc_id, context)
        # explicitly use a new evaluation context since this is a new document
        return self._value_expression(doc, EvaluationContext(doc, 0))

    def __str__(self):
        return "{}[{}]/{}".format(self.related_doc_type,
                                  str(self._doc_id_expression),
                                  str(self._value_expression))
Beispiel #8
0
class IntegerBucketsColumn(_CaseExpressionColumn):
    """Used for grouping by SQL conditionals"""
    type = TypeProperty('integer_buckets')
    _agg_column_type = ConditionalAggregation
    field = StringProperty(required=True)
    ranges = DictProperty()

    def get_whens(self):
        whens = []
        for value, bounds in self.ranges.items():
            if len(bounds) != 2:
                raise BadSpecError(
                    'Range must contain 2 items, contains {}'.format(
                        len(bounds)))
            try:
                bounds = [int(b) for b in bounds]
            except ValueError:
                raise BadSpecError('Invalid range: [{}, {}]'.format(
                    bounds[0], bounds[1]))
            whens.append(
                [self._base_expression(bounds),
                 bindparam(None, value)])
        return whens

    def _base_expression(self, bounds):
        return "{} between {} and {}".format(self.field, bounds[0], bounds[1])
Beispiel #9
0
class ConditionalExpressionSpec(JsonObject):
    type = TypeProperty('conditional')
    test = DictProperty(required=True)
    expression_if_true = DictProperty(required=True)
    expression_if_false = DictProperty(required=True)

    def configure(self, test_function, true_expression, false_expression):
        self._test_function = test_function
        self._true_expression = true_expression
        self._false_expression = false_expression

    def __call__(self, item, context=None):
        if self._test_function(item, context):
            return self._true_expression(item, context)
        else:
            return self._false_expression(item, context)
Beispiel #10
0
class DictExpressionSpec(JsonObject):
    type = TypeProperty('dict')
    properties = DictProperty(required=True)

    def configure(self, compiled_properties):
        for key in compiled_properties:
            if not isinstance(key, (six.text_type, bytes)):
                raise BadSpecError(
                    "Properties in a dict expression must be strings!")
            if six.PY3:
                soft_assert_type_text(key)
        self._compiled_properties = compiled_properties

    def __call__(self, item, context=None):
        ret = {}
        for property_name, expression in self._compiled_properties.items():
            ret[property_name] = expression(item, context)
        return ret

    def __str__(self):
        dict_text = ", ".join([
            "{}:{}".format(name, str(exp))
            for name, exp in self._compiled_properties.items()
        ])
        return "({})".format(dict_text)
Beispiel #11
0
class IteratorExpressionSpec(NoPropertyTypeCoercionMixIn, JsonObject):
    type = TypeProperty('iterator')
    expressions = ListProperty(required=True)
    # an optional filter to test the values on - if they don't match they won't be included in the iteration
    test = DictProperty()

    def configure(self, expressions, test):
        self._expression_fns = expressions
        if test:
            self._test = test
        else:
            # if not defined then all values should be returned
            self._test = lambda *args, **kwargs: True

    def __call__(self, item, context=None):
        values = []
        for expression in self._expression_fns:
            value = expression(item, context)
            if self._test(value):
                values.append(value)
        return values

    def __str__(self):
        expressions_text = ", ".join(str(e) for e in self._expression_fns)
        return "iterate on [{}] if {}".format(expressions_text,
                                              str(self._test))
Beispiel #12
0
class ArrayIndexExpressionSpec(NoPropertyTypeCoercionMixIn, JsonObject):
    type = TypeProperty('array_index')
    array_expression = DictProperty(required=True)
    index_expression = DefaultProperty(required=True)

    def configure(self, array_expression, index_expression):
        self._array_expression = array_expression
        self._index_expression = index_expression

    def __call__(self, item, context=None):
        array_value = self._array_expression(item, context)
        if not isinstance(array_value, list):
            return None

        index_value = self._index_expression(item, context)
        if not isinstance(index_value, int):
            return None

        try:
            return array_value[index_value]
        except IndexError:
            return None

    def __str__(self):
        return "{}[{}]".format(str(self._array_expression),
                               str(self._index_expression))
Beispiel #13
0
class CoalesceExpressionSpec(JsonObject):
    type = TypeProperty('coalesce')
    expression = DictProperty(required=True)
    default_expression = DictProperty(required=True)

    def configure(self, expression, default_expression):
        self._expression = expression
        self._default_expression = default_expression

    def __call__(self, item, context=None):
        expression_value = self._expression(item, context)
        default_value = self._default_expression(item, context)
        if expression_value is None or expression_value == '':
            return default_value
        else:
            return expression_value
Beispiel #14
0
class _GroupsExpressionSpec(JsonObject):
    user_id_expression = DictProperty(required=True)

    def configure(self, user_id_expression):
        self._user_id_expression = user_id_expression

    def __call__(self, item, context=None):
        user_id = self._user_id_expression(item, context)
        if not user_id:
            return []

        assert context.root_doc['domain']
        return self._get_groups(user_id, context)

    def _get_groups(self, user_id, context):
        domain = context.root_doc['domain']
        cache_key = (self.__class__.__name__, domain, user_id)
        if context.get_cache_value(cache_key) is not None:
            return context.get_cache_value(cache_key)

        user = CommCareUser.get_by_user_id(user_id, domain)
        if not user:
            return []

        groups = self._get_groups_from_user(user)
        groups = [g.to_json() for g in groups]
        context.set_cache_value(cache_key, groups)
        return groups

    def _get_groups_from_user(self, user):
        raise NotImplementedError
Beispiel #15
0
class LocationTypeSpec(JsonObject):
    type = TypeProperty('location_type_name')
    location_id_expression = DictProperty(required=True)

    def configure(self, location_id_expression):
        self._location_id_expression = location_id_expression

    def __call__(self, item, context=None):
        doc_id = self._location_id_expression(item, context)
        if not doc_id:
            return None

        assert context.root_doc['domain']
        return self._get_location_type(doc_id, context.root_doc['domain'])

    @staticmethod
    @quickcache(['location_id', 'domain'], timeout=600)
    def _get_location_type(location_id, domain):
        sql_location = SQLLocation.objects.filter(
            domain=domain,
            location_id=location_id
        )
        if sql_location:
            return sql_location[0].location_type.name
        else:
            return None
Beispiel #16
0
class ReportColumn(BaseReportColumn):
    transform = DictProperty()
    calculate_total = BooleanProperty(default=False)

    def format_data(self, data):
        """
        Subclasses can apply formatting to the entire dataset.
        """
        pass

    def get_format_fn(self):
        """
        A function that gets applied to the data just in time before the report is rendered.
        """
        if self.transform:
            return TransformFactory.get_transform(
                self.transform).get_transform_function()
        return None

    def get_query_column_ids(self):
        """
        Gets column IDs associated with a query. These could be different from
        the normal column_ids if the same column ends up in multiple columns in
        the query (e.g. an aggregate date splitting into year and month)
        """
        raise InvalidQueryColumn(
            _("You can't query on columns of type {}".format(self.type)))
Beispiel #17
0
class EvalExpressionSpec(JsonObject):
    type = TypeProperty('evaluator')
    statement = StringProperty(required=True)
    context_variables = DictProperty()
    datatype = DataTypeProperty(required=False)

    def configure(self, context_variables):
        self._context_variables = context_variables

    def __call__(self, item, context=None):
        var_dict = self.get_variables(item, context)
        try:
            untransformed_value = eval_statements(self.statement, var_dict)
            return transform_from_datatype(self.datatype)(untransformed_value)
        except (InvalidExpression, SyntaxError, TypeError, ZeroDivisionError):
            return None

    def get_variables(self, item, context):
        var_dict = {
            slug: variable_expression(item, context)
            for slug, variable_expression in self._context_variables.items()
        }
        return var_dict

    def __str__(self):
        value = self.statement
        for name, exp in self._context_variables.items():
            value.replace(name, str(exp))
        if self.datatype:
            value = "({}){}".format(self.datatype, value)
        return value
Beispiel #18
0
class SplitStringExpressionSpec(JsonObject):
    type = TypeProperty('split_string')
    string_expression = DictProperty(required=True)
    index_expression = DefaultProperty(required=False)
    delimiter = StringProperty(required=False)

    def configure(self, string_expression, index_expression):
        self._string_expression = string_expression
        self._index_expression = index_expression

    def __call__(self, item, context=None):
        string_value = self._string_expression(item, context)
        if not isinstance(string_value, six.string_types):
            return None
        soft_assert_type_text(string_value)

        index_value = None
        if self.index_expression is not None:
            index_value = self._index_expression(item, context)
            if not isinstance(index_value, int):
                return None

        try:
            split = string_value.split(self.delimiter)
            return split[index_value] if index_value is not None else split
        except IndexError:
            return None

    def __str__(self):
        split_text = "split {}".format(str(self._string_expression))
        if self.delimiter:
            split_text += " on '{}'".format(self.delimiter)
        return "(split {})[{}]".format(str(split_text),
                                       str(self._index_expression))
Beispiel #19
0
class SortItemsExpressionSpec(NoPropertyTypeCoercionMixIn, JsonObject):
    ASC, DESC = "ASC", "DESC"
    type = TypeProperty('sort_items')
    items_expression = DefaultProperty(required=True)
    sort_expression = DictProperty(required=True)
    order = StringProperty(choices=[ASC, DESC], default=ASC)

    def configure(self, items_expression, sort_expression):
        self._items_expression = items_expression
        self._sort_expression = sort_expression

    def __call__(self, doc, context=None):
        items = _evaluate_items_expression(self._items_expression, doc,
                                           context)

        try:
            return sorted(items,
                          key=lambda i: self._sort_expression(i, context),
                          reverse=True if self.order == self.DESC else False)
        except TypeError:
            return []

    def __str__(self):
        return "sort:\n{items}\n{order} on:\n{sort}".format(
            items=add_tabbed_text(str(self._items_expression)),
            order=self.order,
            sort=add_tabbed_text(str(self._sort_expression)))
Beispiel #20
0
class ExpressionIndicatorSpec(IndicatorSpecBase):
    type = TypeProperty('expression')
    datatype = DataTypeProperty(required=True)
    is_nullable = BooleanProperty(default=True)
    is_primary_key = BooleanProperty(default=False)
    create_index = BooleanProperty(default=False)
    expression = DefaultProperty(required=True)
    transform = DictProperty(required=False)

    def parsed_expression(self, context):
        from corehq.apps.userreports.expressions.factory import ExpressionFactory
        expression = ExpressionFactory.from_spec(self.expression, context)
        datatype_transform = transform_for_datatype(self.datatype)
        if self.transform:
            generic_transform = TransformFactory.get_transform(
                self.transform).get_transform_function()
            inner_getter = TransformedGetter(expression, generic_transform)
        else:
            inner_getter = expression
        return TransformedGetter(inner_getter, datatype_transform)

    def readable_output(self, context):
        from corehq.apps.userreports.expressions.factory import ExpressionFactory
        expression_object = ExpressionFactory.from_spec(
            self.expression, context)
        return str(expression_object)
Beispiel #21
0
class _GroupsExpressionSpec(JsonObject):
    user_id_expression = DictProperty(required=True)

    def configure(self, user_id_expression):
        self._user_id_expression = user_id_expression

    def __call__(self, item, context=None):
        user_id = self._user_id_expression(item, context)
        if not user_id:
            return []

        assert context.root_doc['domain']
        return self._get_groups(user_id, context)

    @ucr_context_cache(vary_on=('user_id', ))
    def _get_groups(self, user_id, context):
        domain = context.root_doc['domain']
        user = CommCareUser.get_by_user_id(user_id, domain)
        if not user:
            return []

        groups = self._get_groups_from_user(user)
        return [g.to_json() for g in groups]

    def _get_groups_from_user(self, user):
        raise NotImplementedError
Beispiel #22
0
class ConditionalExpressionSpec(JsonObject):
    """
    This expression returns ``"legal" if doc["age"] > 21 else "underage"``:

    .. code:: json

       {
           "type": "conditional",
           "test": {
               "operator": "gt",
               "expression": {
                   "type": "property_name",
                   "property_name": "age",
                   "datatype": "integer"
               },
               "type": "boolean_expression",
               "property_value": 21
           },
           "expression_if_true": {
               "type": "constant",
               "constant": "legal"
           },
           "expression_if_false": {
               "type": "constant",
               "constant": "underage"
           }
       }

    Note that this expression contains other expressions inside it! This is
    why expressions are powerful. (It also contains a filter, but we haven't
    covered those yet - if you find the ``"test"`` section confusing, keep
    reading...)

    Note also that it's important to make sure that you are comparing values
    of the same type. In this example, the expression that retrieves the age
    property from the document also casts the value to an integer. If this
    datatype is not specified, the expression will compare a string to the
    ``21`` value, which will not produce the expected results!
    """
    type = TypeProperty('conditional')
    test = DictProperty(required=True)
    expression_if_true = DefaultProperty(required=True)
    expression_if_false = DefaultProperty(required=True)

    def configure(self, test_function, true_expression, false_expression):
        self._test_function = test_function
        self._true_expression = true_expression
        self._false_expression = false_expression

    def __call__(self, item, context=None):
        if self._test_function(item, context):
            return self._true_expression(item, context)
        else:
            return self._false_expression(item, context)

    def __str__(self):
        return "if {test}:\n{true}\nelse:\n{false}".format(
            test=str(self._test_function),
            true=add_tabbed_text(str(self._true_expression)),
            false=add_tabbed_text(str(self._false_expression)))
class IndexedCaseExpressionSpec(JsonObject):
    type = TypeProperty('indexed_case')
    case_expression = DictProperty(required=True)
    index = StringProperty(required=False)

    def configure(self, case_expression, context):
        self._case_expression = case_expression

        index = self.index or 'parent'
        spec = {
            'type': 'related_doc',
            'related_doc_type': 'CommCareCase',
            'doc_id_expression': {
                'type': 'nested',
                'argument_expression': self.case_expression,
                'value_expression': {
                    'type': 'nested',
                    'argument_expression': {
                        'type': 'array_index',
                        'array_expression': {
                            'type': 'filter_items',
                            'items_expression': {
                                'datatype': 'array',
                                'type': 'property_name',
                                'property_name': 'indices'
                            },
                            'filter_expression': {
                                'type': 'boolean_expression',
                                'operator': 'eq',
                                'property_value': index,
                                'expression': {
                                    'type': 'property_name',
                                    'property_name': 'identifier'
                                }
                            }
                        },
                        'index_expression': {
                            'type': 'constant',
                            'constant': 0
                        }
                    },
                    'value_expression': {
                        'type': 'property_name',
                        'property_name': 'referenced_id'
                    }
                }
            },
            'value_expression': {
                'type': 'identity'
            }
        }
        self._expression = ExpressionFactory.from_spec(spec, context)

    def __call__(self, item, context=None):
        return self._expression(item, context)

    def __str__(self):
        return "{case}/{index}".format(case=str(self._case_expression),
                                       index=self.index or "parent")
Beispiel #24
0
class BooleanIndicatorSpec(IndicatorSpecBase):
    type = TypeProperty('boolean')
    filter = DictProperty(required=True)

    def readable_output(self, context):
        from corehq.apps.userreports.filters.factory import FilterFactory
        filter_object = FilterFactory.from_spec(self.filter, context)
        return str(filter_object)
Beispiel #25
0
class FilterItemsExpressionSpec(NoPropertyTypeCoercionMixIn, JsonObject):
    """
    ``filter_items`` performs filtering on given list and returns a new
    list. If the boolean expression specified by ``filter_expression``
    evaluates to a ``True`` value, the item is included in the new list and
    if not, is not included in the new list.

    ``items_expression`` can be any valid expression that returns a list. If
    this doesn't evaluate to a list an empty list is returned. It may be
    necessary to specify a ``datatype`` of ``array`` if the expression could
    return a single element.

    ``filter_expression`` can be any valid boolean expression relative to
    the items in above list.

    .. code:: json

       {
           "type": "filter_items",
           "items_expression": {
               "datatype": "array",
               "type": "property_name",
               "property_name": "family_repeat"
           },
           "filter_expression": {
              "type": "boolean_expression",
               "expression": {
                   "type": "property_name",
                   "property_name": "gender"
               },
               "operator": "eq",
               "property_value": "female"
           }
       }
    """
    type = TypeProperty('filter_items')
    items_expression = DefaultProperty(required=True)
    filter_expression = DictProperty(required=True)

    def configure(self, items_expression, filter_expression):
        self._items_expression = items_expression
        self._filter_expression = filter_expression

    def __call__(self, doc, context=None):
        items = _evaluate_items_expression(self._items_expression, doc,
                                           context)

        values = []
        for item in items:
            if self._filter_expression(item, context):
                values.append(item)

        return values

    def __str__(self):
        return "filter:\n{items}\non:\n{filter}\n".format(
            items=add_tabbed_text(str(self._items_expression)),
            filter=add_tabbed_text(str(self._filter_expression)))
Beispiel #26
0
class LedgerBalancesIndicatorSpec(IndicatorSpecBase):
    type = TypeProperty('ledger_balances')
    product_codes = ListProperty(required=True)
    ledger_section = StringProperty(required=True)
    case_id_expression = DictProperty(required=True)

    def get_case_id_expression(self):
        from corehq.apps.userreports.expressions.factory import ExpressionFactory
        return ExpressionFactory.from_spec(self.case_id_expression)
Beispiel #27
0
class SplitStringExpressionSpec(JsonObject):
    """
    This expression returns ``(doc["foo bar"]).split(' ')[0]``:

    .. code:: json

       {
           "type": "split_string",
           "string_expression": {
               "type": "property_name",
               "property_name": "multiple_value_string"
           },
           "index_expression": {
               "type": "constant",
               "constant": 0
           },
           "delimiter": ","
       }

    The delimiter is optional and is defaulted to a space. It will return
    nothing if the string_expression is not a string, or if the index isn't
    a number or the indexed item doesn't exist. The index_expression is also
    optional. Without it, the expression will return the list of elements.
    """
    type = TypeProperty('split_string')
    string_expression = DictProperty(required=True)
    index_expression = DefaultProperty(required=False)
    delimiter = StringProperty(required=False)

    def configure(self, string_expression, index_expression):
        self._string_expression = string_expression
        self._index_expression = index_expression

    def __call__(self, item, context=None):
        string_value = self._string_expression(item, context)
        if not isinstance(string_value, str):
            return None

        index_value = None
        if self.index_expression is not None:
            index_value = self._index_expression(item, context)
            if not isinstance(index_value, int):
                return None

        try:
            split = string_value.split(self.delimiter)
            return split[index_value] if index_value is not None else split
        except IndexError:
            return None

    def __str__(self):
        split_text = "split {}".format(str(self._string_expression))
        if self.delimiter:
            split_text += " on '{}'".format(self.delimiter)
        return "(split {})[{}]".format(str(split_text),
                                       str(self._index_expression))
Beispiel #28
0
class BooleanExpressionFilterSpec(BaseFilterSpec):
    type = TypeProperty('boolean_expression')
    operator = StringProperty(choices=OPERATORS.keys(), required=True)
    property_value = DefaultProperty()
    expression = DictProperty(required=True)

    @classmethod
    def wrap(cls, obj):
        _assert_prop_in_obj('property_value', obj)
        return super(BooleanExpressionFilterSpec, cls).wrap(obj)
Beispiel #29
0
class RelatedDocExpressionSpec(JsonObject):
    type = TypeProperty('related_doc')
    related_doc_type = StringProperty()
    doc_id_expression = DictProperty(required=True)
    value_expression = DictProperty(required=True)

    def configure(self, doc_id_expression, value_expression):
        non_couch_doc_types = (LOCATION_DOC_TYPE, )
        if (self.related_doc_type not in non_couch_doc_types
                and get_db_by_doc_type(self.related_doc_type) is None):
            raise BadSpecError(
                u'Cannot determine database for document type {}!'.format(
                    self.related_doc_type))

        self._doc_id_expression = doc_id_expression
        self._value_expression = value_expression

    def __call__(self, item, context=None):
        doc_id = self._doc_id_expression(item, context)
        if doc_id:
            return self.get_value(doc_id, context)

    def _vary_on(self, doc_id, context):
        # For caching. Gets called with the same params as get_value.
        return [
            context.root_doc['domain'], doc_id,
            json.dumps(self.value_expression, sort_keys=True)
        ]

    @quickcache(_vary_on)
    def get_value(self, doc_id, context):
        try:
            assert context.root_doc['domain']
            document_store = get_document_store(context.root_doc['domain'],
                                                self.related_doc_type)
            doc = document_store.get_document(doc_id)
            # ensure no cross-domain lookups of different documents
            if context.root_doc['domain'] != doc.get('domain'):
                return None
            # explicitly use a new evaluation context since this is a new document
            return self._value_expression(doc, EvaluationContext(doc, 0))
        except DocumentNotFoundError:
            return None
Beispiel #30
0
class FormQuestion(JsonObject):
    label = StringProperty()
    translations = DictProperty(exclude_if_none=True)
    tag = StringProperty()
    type = StringProperty(choices=list(VELLUM_TYPES))
    value = StringProperty()
    repeat = StringProperty()
    group = StringProperty()
    options = ListProperty(FormQuestionOption)
    calculate = StringProperty()
    relevant = StringProperty()
    required = BooleanProperty()
    comment = StringProperty()
    setvalue = StringProperty()
    data_source = DictProperty(exclude_if_none=True)

    @property
    def icon(self):
        try:
            return VELLUM_TYPES[self.type]['icon']
        except KeyError:
            return 'fa fa-question-circle'

    @property
    def relative_value(self):
        if self.group:
            prefix = self.group + '/'
            if self.value.startswith(prefix):
                return self.value[len(prefix):]
        return '/'.join(self.value.split('/')[2:])

    @property
    def option_values(self):
        return [o.value for o in self.options]

    @property
    def editable(self):
        if not self.type:
            return True
        vtype = VELLUM_TYPES[self.type]
        if 'editable' not in vtype:
            return False
        return vtype['editable']