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], )
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)]
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)]
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)]
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 ], )
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()
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'), ] ], )
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()