def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() for path, m in self.app.multimedia_map.items(): if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError( '%s does not start with jr://file/commcare/' % path) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) path = '../../' + path multimedia_id = m.multimedia_id yield MediaResource( id=self.id_strings.media_resource(multimedia_id, name), path=path, version=1, local=None, remote=get_url_base() + reverse( 'hqmedia_download', args=[m.media_type, multimedia_id]) + name)
def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() for path, m in self.app.multimedia_map.items(): if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with jr://file/commcare/' % path) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) path = '../../' + path multimedia_id = m.multimedia_id yield MediaResource( id=self.id_strings.media_resource(multimedia_id, name), path=path, version=1, local=None, remote=get_url_base() + reverse( 'hqmedia_download', args=[m.media_type, multimedia_id] ) + name )
def get_bad_usercase_path(module, form, property_): from corehq.apps.app_manager.util import split_path case_id_xpath = get_add_case_preloads_case_id_xpath(module, form) parent_path, property_ = split_path(property_) property_xpath = case_property(property_) id_xpath = get_case_parent_id_xpath(parent_path, case_id_xpath=case_id_xpath) return id_xpath.case().property(property_xpath)
def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() if self.app.multimedia_map is None: self.app.multimedia_map = {} for path, m in self.app.multimedia_map.items(): unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with jr://file/commcare/' % path) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) path = '../../' + path if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id(m.multimedia_id, unchanged_path) yield MediaResource( id=self.id_strings.media_resource(m.unique_id, name), path=path, version=m.version, local=None, remote=get_url_base() + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id] ) + urllib.quote(name.encode('utf-8')) if name else name )
def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() if self.app.multimedia_map is None: self.app.multimedia_map = {} for path, m in self.app.multimedia_map.items(): unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError( '%s does not start with jr://file/commcare/' % path) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) path = '../../' + path if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id( m.multimedia_id, unchanged_path) yield MediaResource( id=self.id_strings.media_resource(m.unique_id, name), path=path, version=m.version, local=None, remote=get_url_base() + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id]) + urllib.quote(name.encode('utf-8')) if name else name)
def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() if self.app.multimedia_map is None: self.app.multimedia_map = {} filter_multimedia = self.app.media_language_map and self.build_profile if filter_multimedia: media_list = [] for lang in self.build_profile.langs: media_list += self.app.media_language_map[lang].media_refs requested_media = set(media_list) for path, m in sorted(list(self.app.multimedia_map.items()), key=lambda item: item[0]): if filter_multimedia and m.form_media and path not in requested_media: continue unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with %s' % (path, PREFIX)) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) install_path = '../../{}'.format(path) local_path = './{}/{}'.format(path, name) if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id( m.multimedia_id, unchanged_path) descriptor = None if self.app.build_version and self.app.build_version >= LooseVersion( '2.9'): type_mapping = { "CommCareImage": "Image", "CommCareAudio": "Audio", "CommCareVideo": "Video", "CommCareMultimedia": "Text" } descriptor = "{filetype} File: {name}".format( filetype=type_mapping.get(m.media_type, "Media"), name=name) yield MediaResource( id=id_strings.media_resource(m.unique_id, name), path=install_path, version=m.version, descriptor=descriptor, local=(local_path if self.app.enable_local_resource else None), remote=self.app.url_base + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id]) + six.moves.urllib.parse.quote(name.encode('utf-8')) if name else name)
def media_resources(self): PREFIX = 'jr://file/' # you have to call remove_unused_mappings # before iterating through multimedia_map self.app.remove_unused_mappings() if self.app.multimedia_map is None: self.app.multimedia_map = {} filter_multimedia = self.app.media_language_map and self.build_profile if filter_multimedia: media_list = [] for lang in self.build_profile.langs: media_list += self.app.media_language_map[lang].media_refs requested_media = set(media_list) for path, m in self.app.multimedia_map.items(): if filter_multimedia and m.form_media and path not in requested_media: continue unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with %s' % (path, PREFIX)) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) install_path = u'../../{}'.format(path) local_path = u'./{}/{}'.format(path, name) if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id(m.multimedia_id, unchanged_path) descriptor = None if self.app.build_version >= '2.9': type_mapping = {"CommCareImage": "Image", "CommCareAudio": "Audio", "CommCareVideo": "Video", "CommCareMultimedia": "Text"} descriptor = u"{filetype} File: {name}".format( filetype=type_mapping.get(m.media_type, "Media"), name=name ) yield MediaResource( id=id_strings.media_resource(m.unique_id, name), path=install_path, version=m.version, descriptor=descriptor, local=(local_path if self.app.enable_local_resource else None), remote=self.app.url_base + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id] ) + urllib.quote(name.encode('utf-8')) if name else name )
def group_updates_by_case(updates): """ updates grouped by case. Example: input: {'name': ..., 'parent/name'} output: {'': {'name': ...}, 'parent': {'name': ...}} """ updates_by_case = defaultdict(dict) for key, value in updates.items(): path, name = split_path(key) updates_by_case[path][name] = value return updates_by_case
def make_nested_subnode(base_node, path): """ path='x/y/z' will append <x><y><z/></y></x> to base_node """ prev_node = base_node tail, head = split_path(path) if tail: for node_name in tail.split('/'): prev_node = prev_node.find('{x}%s' % node_name) node = _make_elem('{x}%s' % head) prev_node.append(node) return node
def media_resources(self): PREFIX = 'jr://file/' multimedia_map = self.app.multimedia_map_for_build( build_profile=self.build_profile, remove_unused=True) lazy_load_preference = self.app.profile.get( 'properties', {}).get('lazy-load-video-files') for path, m in sorted(list(multimedia_map.items()), key=lambda item: item[0]): unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with %s' % (path, PREFIX)) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) install_path = '../../{}'.format(path) local_path = './{}/{}'.format(path, name) load_lazily = (lazy_load_preference == 'true' and m.media_type == "CommCareVideo") if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id( m.multimedia_id, unchanged_path) descriptor = None if self.app.build_version and self.app.build_version >= LooseVersion( '2.9'): type_mapping = { "CommCareImage": "Image", "CommCareAudio": "Audio", "CommCareVideo": "Video", "CommCareMultimedia": "Text" } descriptor = "{filetype} File: {name}".format( filetype=type_mapping.get(m.media_type, "Media"), name=name) yield MediaResource( id=id_strings.media_resource(m.unique_id, name), path=install_path, version=m.version, descriptor=descriptor, lazy=load_lazily, local=(local_path if self.app.enable_local_resource else None), remote=self.app.url_base + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id]) + six.moves.urllib.parse.quote(name.encode('utf-8')) if name else name)
def media_resources(self): PREFIX = 'jr://file/' for path, m in sorted(list(self.app.multimedia_map_for_build(build_profile=self.build_profile).items()), key=lambda item: item[0]): unchanged_path = path if path.startswith(PREFIX): path = path[len(PREFIX):] else: raise MediaResourceError('%s does not start with %s' % (path, PREFIX)) path, name = split_path(path) # CommCare assumes jr://media/, # which is an alias to jr://file/commcare/media/ # so we need to replace 'jr://file/' with '../../' # (this is a hack) install_path = '../../{}'.format(path) local_path = './{}/{}'.format(path, name) if not getattr(m, 'unique_id', None): # lazy migration for adding unique_id to map_item m.unique_id = HQMediaMapItem.gen_unique_id(m.multimedia_id, unchanged_path) descriptor = None if self.app.build_version and self.app.build_version >= LooseVersion('2.9'): type_mapping = {"CommCareImage": "Image", "CommCareAudio": "Audio", "CommCareVideo": "Video", "CommCareMultimedia": "Text"} descriptor = "{filetype} File: {name}".format( filetype=type_mapping.get(m.media_type, "Media"), name=name ) yield MediaResource( id=id_strings.media_resource(m.unique_id, name), path=install_path, version=m.version, descriptor=descriptor, local=(local_path if self.app.enable_local_resource else None), remote=self.app.url_base + reverse( 'hqmedia_download', args=[m.media_type, m.multimedia_id] ) + six.moves.urllib.parse.quote(name.encode('utf-8')) if name else name )
def check_case_properties(self, all_names=None, subcase_names=None, case_tag=None): all_names = all_names or [] subcase_names = subcase_names or [] errors = [] reserved_words = load_case_reserved_words() for key in all_names: try: validate_property(key) except ValueError: errors.append({'type': 'update_case word illegal', 'word': key, 'case_tag': case_tag}) _, key = split_path(key) if key in reserved_words: errors.append({'type': 'update_case uses reserved word', 'word': key, 'case_tag': case_tag}) # no parent properties for subcase for key in subcase_names: if not re.match(r'^[a-zA-Z][\w_-]*$', key): errors.append({'type': 'update_case word illegal', 'word': key, 'case_tag': case_tag}) return errors
def create_casexml_2(self, form): from corehq.apps.app_manager.util import split_path actions = form.active_actions() if form.requires == 'none' and 'open_case' not in actions and 'update_case' in actions: raise CaseError("To update a case you must either open a case or require a case to begin with") def make_case_elem(tag, attr=None): return _make_elem('{cx2}%s' % tag, attr) def make_case_block(path=''): case_block = ET.Element('{cx2}case'.format(**namespaces), { 'case_id': '', 'date_modified': '', 'user_id': '', }, nsmap={ None: namespaces['cx2'][1:-1] }) self.add_bind( nodeset="%scase/@date_modified" % path, type="dateTime", calculate=self.resolve_path("meta/timeEnd") ) self.add_bind( nodeset="%scase/@user_id" % path, calculate=self.resolve_path("meta/userID"), ) return case_block def relevance(action): if action.condition.type == 'always': return 'true()' elif action.condition.type == 'if': return "%s = '%s'" % (self.resolve_path(action.condition.question), action.condition.answer) else: return 'false()' def add_create_block(case_block, action, case_name, case_type, path='', delay_case_id=False): create_block = make_case_elem('create') case_block.append(create_block) case_type_node = make_case_elem('case_type') case_type_node.text = case_type create_block.extend([ make_case_elem('case_name'), make_case_elem('owner_id'), case_type_node, ]) self.add_bind( nodeset='%scase' % path, relevant=relevance(action), ) if not delay_case_id: self.add_setvalue( ref='%scase/@case_id' % path, value='uuid()', ) else: self.add_bind( nodeset='%scase/@case_id' % path, calculate='uuid()', # relevant='count(%scase) > 0' % path, ) self.add_bind( nodeset="%scase/create/case_name" % path, calculate=self.resolve_path(case_name), ) if form.get_app().case_sharing: self.add_instance('groups', src='jr://fixture/user-groups') self.add_setvalue( ref="%scase/create/owner_id" % path, value="instance('groups')/groups/group/@id" ) else: self.add_bind( nodeset="%scase/create/owner_id" % path, calculate=self.resolve_path("meta/userID"), ) if not case_name: raise CaseError("Please set 'Name according to question'. " "This will give each case a 'name' attribute") self.add_bind( nodeset=case_name, required="true()", ) def add_update_block(case_block, updates, path=''): update_block = make_case_elem('update') case_block.append(update_block) update_mapping = {} if updates: for key, value in updates.items(): if key == 'name': key = 'case_name' update_mapping[key] = value for key in sorted(update_mapping.keys()): update_block.append(make_case_elem(key)) for key, q_path in sorted(update_mapping.items()): self.add_bind( nodeset="%scase/update/%s" % (path, key), calculate=self.resolve_path(q_path), relevant=("count(%s) > 0" % self.resolve_path(q_path)) ) def add_close_block(case_block, action=None, path=''): case_block.append(make_case_elem('close')) self.add_bind( nodeset="%scase/close" % path, relevant=relevance(action) if action else 'true()', ) def get_case_parent_xpath(parent_path): xpath = SESSION_CASE_ID.case() if parent_path: for parent_name in parent_path.split('/'): xpath = xpath.index_id(parent_name).case() return xpath delegation_case_block = None if not actions or (form.requires == 'none' and 'open_case' not in actions): case_block = None else: extra_updates = {} needs_casedb_instance = False case_block = make_case_block() if form.requires != 'none': def make_delegation_stub_case_block(): path = 'cc_delegation_stub/' DELEGATION_ID = 'delegation_id' outer_block = _make_elem('{x}cc_delegation_stub', {DELEGATION_ID: ''}) delegation_case_block = make_case_block(path) add_close_block(delegation_case_block) session_delegation_id = "instance('commcaresession')/session/data/%s" % DELEGATION_ID path_to_delegation_id = self.resolve_path("%s@%s" % (path, DELEGATION_ID)) self.add_setvalue( ref="%s@%s" % (path, DELEGATION_ID), value="if(count({d}) = 1, {d}, '')".format(d=session_delegation_id), ) self.add_bind( nodeset="%scase" % path, relevant="%s != ''" % path_to_delegation_id, ) self.add_bind( nodeset="%scase/@case_id" % path, calculate=path_to_delegation_id ) outer_block.append(delegation_case_block) return outer_block if form.get_module().task_list.show: delegation_case_block = make_delegation_stub_case_block() if 'open_case' in actions: open_case_action = actions['open_case'] add_create_block(case_block, open_case_action, case_name=open_case_action.name_path, case_type=form.get_case_type()) if 'external_id' in actions['open_case'] and actions['open_case'].external_id: extra_updates['external_id'] = actions['open_case'].external_id else: self.add_bind( nodeset="case/@case_id", calculate=SESSION_CASE_ID, ) if 'update_case' in actions or extra_updates: def group_updates_by_case(updates): """ updates grouped by case. Example: input: {'name': ..., 'parent/name'} output: {'': {'name': ...}, 'parent': {'name': ...}} """ updates_by_case = defaultdict(dict) for key, value in updates.items(): path, name = split_path(key) updates_by_case[path][name] = value return updates_by_case updates_by_case = group_updates_by_case( getattr(actions.get('update_case'), 'update', {}) ) updates_by_case[''].update(extra_updates) if '' in updates_by_case: # 90% use-case basic_updates = updates_by_case.pop('') add_update_block(case_block, basic_updates) if updates_by_case: def make_nested_subnode(base_node, path): """ path='x/y/z' will append <x><y><z/></y></x> to base_node """ prev_node = base_node tail, head = split_path(path) if tail: for node_name in tail.split('/'): prev_node = prev_node.find('{x}%s' % node_name) node = _make_elem('{x}%s' % head) prev_node.append(node) return node def make_parent_case_block(node_path, parent_path): case_block = make_case_block(node_path) xpath = get_case_parent_xpath(parent_path) self.add_bind( nodeset='%scase/@case_id' % node_path, calculate=xpath.property('@case_id'), ) return case_block base_node = _make_elem('{x}parents') self.data_node.append(base_node) for parent_path, updates in sorted(updates_by_case.items()): node = make_nested_subnode(base_node, parent_path) node_path = 'parents/%s/' % parent_path parent_case_block = make_parent_case_block(node_path, parent_path) add_update_block(parent_case_block, updates, node_path) node.append(parent_case_block) if 'close_case' in actions: add_close_block(case_block, actions['close_case']) if 'case_preload' in actions: needs_casedb_instance = True for nodeset, property in actions['case_preload'].preload.items(): parent_path, property = split_path(property) property_xpath = { 'name': 'case_name', 'owner_id': '@owner_id' }.get(property, property) xpath = get_case_parent_xpath(parent_path) self.add_setvalue( ref=nodeset, value=xpath.property(property_xpath), ) if needs_casedb_instance: self.add_instance('casedb', src='jr://instance/casedb') if 'subcases' in actions: for i, subcase in enumerate(actions['subcases']): if subcase.repeat_context: name = subcase.repeat_context _xpath = '/{x}'.join(subcase.repeat_context.split('/'))[1:] subcase_node = self.instance_node.find(_xpath) else: name = 'subcase_%s' % i subcase_node = _make_elem('{x}%s' % name) self.data_node.append(subcase_node) path = '%s/' % name subcase_block = make_case_block(path) subcase_node.insert(0, subcase_block) add_create_block( subcase_block, subcase, case_name=subcase.case_name, case_type=subcase.case_type, path=path, delay_case_id=bool(subcase.repeat_context), ) add_update_block(subcase_block, subcase.case_properties, path=path) if case_block is not None and subcase.case_type != form.get_case_type(): index_node = make_case_elem('index') parent_index = make_case_elem('parent', {'case_type': form.get_case_type()}) self.add_bind( nodeset='%s/case/index/parent' % name, calculate=self.resolve_path("case/@case_id"), ) index_node.append(parent_index) subcase_block.append(index_node) # always needs session instance for meta self.add_instance('commcaresession', src='jr://instance/session') case = self.case_node case_parent = self.data_node if case_block is not None: if case.exists(): raise XFormError("You cannot use the Case Management UI if you already have a case block in your form.") else: case_parent.append(case_block) if delegation_case_block is not None: case_parent.append(delegation_case_block) if not case_parent.exists(): raise XFormError("Couldn't get the case XML from one of your forms. " "A common reason for this is if you don't have the " "xforms namespace defined in your form. Please verify " 'that the xmlns="http://www.w3.org/2002/xforms" ' "attribute exists in your form.")
def create_casexml_2(self, form): from corehq.apps.app_manager.util import split_path actions = form.active_actions() if form.requires == 'none' and 'open_case' not in actions and 'update_case' in actions: raise CaseError( "To update a case you must either open a case or require a case to begin with" ) def make_case_elem(tag, attr=None): return _make_elem('{cx2}%s' % tag, attr) def make_case_block(path=''): case_block = ET.Element('{cx2}case'.format(**namespaces), { 'case_id': '', 'date_modified': '', 'user_id': '', }, nsmap={None: namespaces['cx2'][1:-1]}) self.add_bind(nodeset="%scase/@date_modified" % path, type="dateTime", calculate=self.resolve_path("meta/timeEnd")) self.add_bind( nodeset="%scase/@user_id" % path, calculate=self.resolve_path("meta/userID"), ) return case_block def relevance(action): if action.condition.type == 'always': return 'true()' elif action.condition.type == 'if': return "%s = '%s'" % (self.resolve_path( action.condition.question), action.condition.answer) else: return 'false()' def add_create_block(case_block, action, case_name, case_type, path=''): create_block = make_case_elem('create') case_block.append(create_block) case_type_node = make_case_elem('case_type') case_type_node.text = case_type create_block.extend([ make_case_elem('case_name'), make_case_elem('owner_id'), case_type_node, ]) self.add_bind( nodeset='%scase' % path, relevant=relevance(action), ) self.add_setvalue( ref="%scase/@case_id" % path, value="uuid()", ) self.add_bind( nodeset="%scase/create/case_name" % path, calculate=self.resolve_path(case_name), ) if form.get_app().case_sharing: self.add_instance('groups', src='jr://fixture/user-groups') self.add_setvalue(ref="%scase/create/owner_id" % path, value="instance('groups')/groups/group/@id") else: self.add_bind( nodeset="%scase/create/owner_id" % path, calculate=self.resolve_path("meta/userID"), ) if not case_name: raise CaseError("Please set 'Name according to question'. " "This will give each case a 'name' attribute") self.add_bind( nodeset=case_name, required="true()", ) def add_update_block(case_block, updates, path=''): update_block = make_case_elem('update') case_block.append(update_block) update_mapping = {} if updates: for key, value in updates.items(): if key == 'name': key = 'case_name' update_mapping[key] = value for key in sorted(update_mapping.keys()): update_block.append(make_case_elem(key)) for key, q_path in sorted(update_mapping.items()): self.add_bind(nodeset="%scase/update/%s" % (path, key), calculate=self.resolve_path(q_path), relevant=("count(%s) > 0" % self.resolve_path(q_path))) def add_close_block(case_block, action=None, path=''): case_block.append(make_case_elem('close')) self.add_bind( nodeset="%scase/close" % path, relevant=relevance(action) if action else 'true()', ) def get_case_parent_xpath(parent_path): xpath = SESSION_CASE_ID.case() if parent_path: for parent_name in parent_path.split('/'): xpath = xpath.index_id(parent_name).case() return xpath delegation_case_block = None if not actions or (form.requires == 'none' and 'open_case' not in actions): case_block = None else: extra_updates = {} needs_casedb_instance = False case_block = make_case_block() if form.requires != 'none': def make_delegation_stub_case_block(): path = 'cc_delegation_stub/' DELEGATION_ID = 'delegation_id' outer_block = _make_elem('{x}cc_delegation_stub', {DELEGATION_ID: ''}) delegation_case_block = make_case_block(path) add_close_block(delegation_case_block) session_delegation_id = "instance('commcaresession')/session/data/%s" % DELEGATION_ID path_to_delegation_id = self.resolve_path( "%s@%s" % (path, DELEGATION_ID)) self.add_setvalue( ref="%s@%s" % (path, DELEGATION_ID), value="if(count({d}) = 1, {d}, '')".format( d=session_delegation_id), ) self.add_bind( nodeset="%scase" % path, relevant="%s != ''" % path_to_delegation_id, ) self.add_bind(nodeset="%scase/@case_id" % path, calculate=path_to_delegation_id) outer_block.append(delegation_case_block) return outer_block if form.get_module().task_list.show: delegation_case_block = make_delegation_stub_case_block() if 'open_case' in actions: open_case_action = actions['open_case'] add_create_block(case_block, open_case_action, case_name=open_case_action.name_path, case_type=form.get_case_type()) if 'external_id' in actions['open_case'] and actions[ 'open_case'].external_id: extra_updates['external_id'] = actions[ 'open_case'].external_id else: self.add_bind( nodeset="case/@case_id", calculate=SESSION_CASE_ID, ) if 'update_case' in actions or extra_updates: def group_updates_by_case(updates): """ updates grouped by case. Example: input: {'name': ..., 'parent/name'} output: {'': {'name': ...}, 'parent': {'name': ...}} """ updates_by_case = defaultdict(dict) for key, value in updates.items(): path, name = split_path(key) updates_by_case[path][name] = value return updates_by_case updates_by_case = group_updates_by_case( getattr(actions.get('update_case'), 'update', {})) updates_by_case[''].update(extra_updates) if '' in updates_by_case: # 90% use-case basic_updates = updates_by_case.pop('') add_update_block(case_block, basic_updates) if updates_by_case: def make_nested_subnode(base_node, path): """ path='x/y/z' will append <x><y><z/></y></x> to base_node """ prev_node = base_node tail, head = split_path(path) if tail: for node_name in tail.split('/'): prev_node = prev_node.find('{x}%s' % node_name) node = _make_elem('{x}%s' % head) prev_node.append(node) return node def make_parent_case_block(node_path, parent_path): case_block = make_case_block(node_path) xpath = get_case_parent_xpath(parent_path) self.add_bind( nodeset='%scase/@case_id' % node_path, calculate=xpath.property('@case_id'), ) return case_block base_node = _make_elem('{x}parents') self.data_node.append(base_node) for parent_path, updates in sorted( updates_by_case.items()): node = make_nested_subnode(base_node, parent_path) node_path = 'parents/%s/' % parent_path parent_case_block = make_parent_case_block( node_path, parent_path) add_update_block(parent_case_block, updates, node_path) node.append(parent_case_block) if 'close_case' in actions: add_close_block(case_block, actions['close_case']) if 'case_preload' in actions: needs_casedb_instance = True for nodeset, property in actions['case_preload'].preload.items( ): parent_path, property = split_path(property) property_xpath = { 'name': 'case_name', 'owner_id': '@owner_id' }.get(property, property) xpath = get_case_parent_xpath(parent_path) self.add_setvalue( ref=nodeset, value=xpath.property(property_xpath), ) if needs_casedb_instance: self.add_instance('casedb', src='jr://instance/casedb') if 'subcases' in actions: for i, subcase in enumerate(actions['subcases']): name = 'subcase_%s' % i path = '%s/' % name subcase_node = _make_elem('{x}%s' % name) subcase_block = make_case_block(path) add_create_block(subcase_block, subcase, case_name=subcase.case_name, case_type=subcase.case_type, path=path) add_update_block(subcase_block, subcase.case_properties, path=path) if case_block is not None and subcase.case_type != form.get_case_type( ): index_node = make_case_elem('index') parent_index = make_case_elem( 'parent', {'case_type': form.get_case_type()}) self.add_bind( nodeset='%s/case/index/parent' % name, calculate=self.resolve_path("case/@case_id"), ) index_node.append(parent_index) subcase_block.append(index_node) subcase_node.append(subcase_block) self.data_node.append(subcase_node) # always needs session instance for meta self.add_instance('commcaresession', src='jr://instance/session') case = self.case_node case_parent = self.data_node if case_block is not None: if case.exists(): raise XFormError( "You cannot use the Case Management UI if you already have a case block in your form." ) else: case_parent.append(case_block) if delegation_case_block is not None: case_parent.append(delegation_case_block)