Exemplo n.º 1
0
def get_form_data_source(app, form):
    xform = XForm(form.source)
    form_name = form.default_name()
    questions = xform.get_questions([])

    return DataSourceConfiguration(
        domain=app.domain,
        referenced_doc_type="XFormInstance",
        table_id=_clean_table_name(app.domain, form_name),
        display_name=form_name,
        configured_filter=make_form_data_source_filter(xform.data_node.tag_xmlns),
        configured_indicators=[
            make_form_question_indicator(q, column_id=get_column_name(q["value"])) for q in questions
        ]
        + [make_form_meta_block_indicator(field) for field in FORM_METADATA_PROPERTIES],
    )
Exemplo n.º 2
0
    def get_questions(form):
        xform = XForm(form.source)
        prefix = '/%s/' % xform.data_node.tag_name

        def remove_prefix(string):
            if string.startswith(prefix):
                return string[len(prefix):]
            else:
                raise Exception()

        def transform_question(q):
            return {
                'id': remove_prefix(q['value']),
                'type': q['tag'],
                'text': q['label'] if q['tag'] != 'hidden' else ''
            }
        return [transform_question(q) for q in xform.get_questions(langs)]
Exemplo n.º 3
0
    def get_questions(form):
        xform = XForm(form.source)
        prefix = '/%s/' % xform.data_node.tag_name

        def remove_prefix(string):
            if string.startswith(prefix):
                return string[len(prefix):]
            else:
                raise Exception()

        def transform_question(q):
            return {
                'id': remove_prefix(q['value']),
                'type': q['tag'],
                'text': q['label'] if q['tag'] != 'hidden' else ''
            }
        return [transform_question(q) for q in xform.get_questions(langs)]
Exemplo n.º 4
0
    def get_questions(form):
        xform = XForm(form.source)
        prefix = "/%s/" % xform.data_node.tag_name

        def remove_prefix(string):
            if string.startswith(prefix):
                return string[len(prefix) :]
            else:
                raise Exception()

        def transform_question(q):
            return {
                "id": remove_prefix(q["value"]),
                "type": q["tag"],
                "text": q["label"] if q["tag"] != "hidden" else "",
            }

        return [transform_question(q) for q in xform.get_questions(langs)]
Exemplo n.º 5
0
def get_form_data_source(app, form):
    xform = XForm(form.source)
    form_name = form.default_name()
    questions = xform.get_questions([])

    return DataSourceConfiguration(
        domain=app.domain,
        referenced_doc_type='XFormInstance',
        table_id=_clean_table_name(app.domain, form_name),
        display_name=form_name,
        configured_filter=make_form_data_source_filter(xform.data_node.tag_xmlns),
        configured_indicators=[
            make_form_question_indicator(q, column_id=get_column_name(q['value']))
            for q in questions
        ] + [
            make_form_meta_block_indicator(field)
            for field in FORM_METADATA_PROPERTIES
        ],
    )
Exemplo n.º 6
0
class DataSourceBuilder(object):
    """
    When configuring a report, one can use DataSourceBuilder to determine some
    of the properties of the required report data source, such as:
        - referenced doc type
        - filter
        - indicators
    """

    def __init__(self, domain, app, source_type, source_id):
        assert (source_type in ['case', 'form'])

        self.domain = domain
        self.app = app
        self.source_type = source_type
        # source_id is a case type of form id
        self.source_id = source_id
        if self.source_type == 'form':
            self.source_form = Form.get_form(self.source_id)
            self.source_xform = XForm(self.source_form.source)
        if self.source_type == 'case':
            property_builder = ParentCasePropertyBuilder(
                self.app, DEFAULT_CASE_PROPERTY_DATATYPES.keys()
            )
            self.case_properties = list(
                property_builder.get_properties(self.source_id) | {'closed'}
            )

    @property
    @memoized
    def source_doc_type(self):
        if self.source_type == "case":
            return "CommCareCase"
        if self.source_type == "form":
            return "XFormInstance"

    @property
    @memoized
    def filter(self):
        """
        Return the filter configuration for the DataSourceConfiguration.
        """
        if self.source_type == "case":
            return make_case_data_source_filter(self.source_id)
        if self.source_type == "form":
            return make_form_data_source_filter(self.source_xform.data_node.tag_xmlns)

    @property
    @memoized
    def indicators(self):
        """
        Return all the dict data source indicator configurations that could be
        used by a report that uses the same case type/form as this DataSourceConfiguration.
        """
        ret = []
        for prop in self.data_source_properties.values():
            if prop['type'] == 'meta':
                ret.append(make_form_meta_block_indicator(
                    prop['source'], prop['column_id']
                ))
            elif prop['type'] == "question":
                ret.append(make_form_question_indicator(
                    prop['source'], prop['column_id']
                ))
            elif prop['type'] == 'case_property':
                ret.append(make_case_property_indicator(
                    prop['source'], prop['column_id']
                ))
        ret.append({
            "display_name": "Count",
            "type": "count",
            "column_id": "count"
        })
        return ret

    @property
    @memoized
    def data_source_properties(self):
        """
        A dictionary containing the various properties that may be used as indicators
        or columns in the data source or report.

        Keys are strings that uniquely identify properties.
        Values are dicts representing the properties, ex:

        >> self.data_source_properties
        {
            "/data/question1": {
                "type": "question",
                "id": "/data/question1",
                "text": "Enter the child's name",
                "column_id": "data--question1",
                "source": {
                    'repeat': None,
                    'group': None,
                    'value': '/data/question1',
                    'label': 'question1',
                    'tag': 'input',
                    'type': 'Text'
                }
            },
            "meta/deviceID": {
                "type": "meta",
                "id": "meta/deviceID",
                "text": "deviceID",
                "column_id": "meta--deviceID",
                "source": ("deviceID", "string")
            }
        }

        "id" is used as the value in selects/select2s in the form. Uniquely identifies questions.
        "column_id" is used as the column name for this indicator. There are bugs
        with slashes which requires this to be different from "id"
        "text" will be used as the visible text in selects/select2s
        "type" is "question", "case_property", or "meta"
        For questions, "source" is the dict returned by Xform.get_questions, for
        case properties and form metadata it is simply the name of the property.
        """

        if self.source_type == 'case':
            return {
                cp: {
                    'type': 'case_property',
                    'id': cp,
                    'column_id': get_column_name(cp),
                    'text': cp,
                    'source': cp
                } for cp in self.case_properties
            }

        if self.source_type == 'form':
            ret = {}
            questions = self.source_xform.get_questions([])
            ret.update({
                q['value']: {
                    "type": "question",
                    "id": q['value'],
                    "column_id": get_column_name(q['value'].strip("/")),
                    'text': q['label'],
                    "source": q,
                } for q in questions
            })
            ret.update({
                p[0]: {
                    "type": "meta",
                    "id": p[0],
                    "column_id": get_column_name(p[0].strip("/")),
                    'text': p[0],
                    "source": p,
                } for p in FORM_METADATA_PROPERTIES
            })
            return ret

    @property
    @memoized
    def data_source_name(self):
        if self.source_type == 'form':
            return "{} (v{})".format(self.source_form.default_name(), self.app.version)
        if self.source_type == 'case':
            return "{} (v{})".format(self.source_id, self.app.version)

    def get_existing_match(self):
        return DataSourceConfiguration.view(
            'userreports/data_sources_by_build_info',
            key=[
                self.domain,
                self.source_doc_type,
                self.source_id,
                self.app._id,
                self.app.version
            ],
            reduce=False
        ).one()
Exemplo n.º 7
0
def get_form_data_source(app, form):
    xform = XForm(form.source)
    form_name = form.default_name()

    def _get_indicator_data_type(data_type, options):
        if data_type == "date":
            return {"datatype": "date"}
        if data_type == "MSelect":
            return {
                "type": "choice_list",
                "select_style": DATATYPE_MAP[data_type],
                "choices": [
                    option['value'] for option in options
                ],
            }
        return {"datatype": "string"}

    def _make_indicator(question):
        path = question['value'].split('/')
        data_type = question['type']
        options = question.get('options')
        ret = {
            "type": "raw",
            "column_id": path[-1],
            'property_path': ['form'] + path[2:],
            "display_name": path[-1],
        }
        ret.update(_get_indicator_data_type(data_type, options))
        return ret

    def _make_meta_block_indicator(field_name, data_type):
        ret = {
            "type": "raw",
            "column_id": field_name,
            "property_path": ['form', 'meta'] + [field_name],
            "display_name": field_name,
        }
        ret.update(_get_indicator_data_type(data_type, []))
        return ret

    questions = xform.get_questions([])

    return DataSourceConfiguration(
        domain=app.domain,
        referenced_doc_type='XFormInstance',
        table_id=_clean_table_name(app.domain, form_name),
        display_name=form_name,
        configured_filter={
            "type": "property_match",
            "property_name": "xmlns",
            "property_path": [],
            "property_value": xform.data_node.tag_xmlns
        },
        configured_indicators=[
            _make_indicator(q) for q in questions
        ] + [
            _make_meta_block_indicator(field[0], field[1]) for field in [
                ('username', 'string'),
                ('userID', 'string'),
                ('timeStart', 'datetime'),
                ('timeEnd', 'datetime'),
                ('deviceID', 'string'),
            ]
        ],
    )
Exemplo n.º 8
0
class DataSourceBuilder(object):
    """
    When configuring a report, one can use DataSourceBuilder to determine some
    of the properties of the required report data source, such as:
        - referenced doc type
        - filter
        - indicators
    """
    def __init__(self, domain, app, source_type, source_id):
        assert (source_type in ['case', 'form'])

        self.domain = domain
        self.app = app
        self.source_type = source_type
        # source_id is a case type of form id
        self.source_id = source_id
        if self.source_type == 'form':
            self.source_form = Form.get_form(self.source_id)
            self.source_xform = XForm(self.source_form.source)
        if self.source_type == 'case':
            prop_map = get_case_properties(
                self.app, [self.source_id],
                defaults=DEFAULT_CASE_PROPERTY_DATATYPES.keys())
            self.case_properties = sorted(
                set(prop_map[self.source_id]) | {'closed'})

    @property
    @memoized
    def source_doc_type(self):
        if self.source_type == "case":
            return "CommCareCase"
        if self.source_type == "form":
            return "XFormInstance"

    @property
    @memoized
    def filter(self):
        """
        Return the filter configuration for the DataSourceConfiguration.
        """
        if self.source_type == "case":
            return make_case_data_source_filter(self.source_id)
        if self.source_type == "form":
            return make_form_data_source_filter(
                self.source_xform.data_node.tag_xmlns)

    @property
    @memoized
    def indicators(self):
        """
        Return all the dict data source indicator configurations that could be
        used by a report that uses the same case type/form as this DataSourceConfiguration.
        """
        ret = []
        for prop in self.data_source_properties.values():
            if prop['type'] == 'meta':
                ret.append(
                    make_form_meta_block_indicator(prop['source'],
                                                   prop['column_id']))
            elif prop['type'] == "question":
                ret.append(
                    make_form_question_indicator(prop['source'],
                                                 prop['column_id']))
            elif prop['type'] == 'case_property' and prop[
                    'source'] == 'computed/owner_name':
                ret.append(make_owner_name_indicator(prop['column_id']))
            elif prop['type'] == 'case_property':
                ret.append(
                    make_case_property_indicator(prop['source'],
                                                 prop['column_id']))
        ret.append({
            "display_name": "Count",
            "type": "count",
            "column_id": "count"
        })
        return ret

    @property
    @memoized
    def data_source_properties(self):
        """
        A dictionary containing the various properties that may be used as indicators
        or columns in the data source or report.

        Keys are strings that uniquely identify properties.
        Values are dicts representing the properties, ex:

        >> self.data_source_properties
        {
            "/data/question1": {
                "type": "question",
                "id": "/data/question1",
                "text": "Enter the child's name",
                "column_id": "data--question1",
                "source": {
                    'repeat': None,
                    'group': None,
                    'value': '/data/question1',
                    'label': 'question1',
                    'tag': 'input',
                    'type': 'Text'
                }
            },
            "meta/deviceID": {
                "type": "meta",
                "id": "meta/deviceID",
                "text": "deviceID",
                "column_id": "meta--deviceID",
                "source": ("deviceID", "string")
            }
        }

        "id" is used as the value in selects/select2s in the form. Uniquely identifies questions.
        "column_id" is used as the column name for this indicator. There are bugs
        with slashes which requires this to be different from "id"
        "text" will be used as the visible text in selects/select2s
        "type" is "question", "case_property", or "meta"
        For questions, "source" is the dict returned by Xform.get_questions, for
        case properties and form metadata it is simply the name of the property.
        """

        if self.source_type == 'case':
            ret = OrderedDict((cp, {
                'type': 'case_property',
                'id': cp,
                'column_id': get_column_name(cp),
                'text': cp,
                'source': cp
            }) for cp in self.case_properties)
            ret['computed/owner_name'] = {
                'type': 'case_property',
                'id': 'computed/owner_name',
                'column_id': get_column_name('computed/owner_name'),
                'text': 'owner_name (computed)',
                'source': 'computed/owner_name'
            }
            return ret

            # Note that owner_name is a special pseudo-case property.
            # The report builder will create a related_doc indicator based
            # on the owner_id of the case.

        if self.source_type == 'form':
            ret = OrderedDict()
            questions = self.source_xform.get_questions([])
            ret.update((q['value'], {
                "type": "question",
                "id": q['value'],
                "column_id": get_column_name(q['value'].strip("/")),
                'text': q['label'],
                "source": q,
            }) for q in questions)
            ret.update((p[0], {
                "type": "meta",
                "id": p[0],
                "column_id": get_column_name(p[0].strip("/")),
                'text': p[0],
                "source": p,
            }) for p in FORM_METADATA_PROPERTIES)
            return ret

    @property
    @memoized
    def data_source_name(self):
        if self.source_type == 'form':
            return "{} (v{})".format(self.source_form.default_name(),
                                     self.app.version)
        if self.source_type == 'case':
            return "{} (v{})".format(self.source_id, self.app.version)

    def get_existing_match(self):
        return DataSourceConfiguration.view(
            'userreports/data_sources_by_build_info',
            key=[
                self.domain, self.source_doc_type, self.source_id,
                self.app._id, self.app.version
            ],
            reduce=False).one()