def _get_or_update_cases(xforms, case_db):
    """
    Given an xform document, update any case blocks found within it,
    returning a dictionary mapping the case ids affected to the
    couch case document objects
    """
    domain = getattr(case_db, 'domain', None)
    touched_cases = FormProcessorInterface(domain).get_cases_from_forms(case_db, xforms)

    # once we've gotten through everything, validate all indices
    # and check for new dirtiness flags
    def _validate_indices(case):
        if case.indices:
            for index in case.indices:
                # call get and not doc_exists to force domain checking
                # see CaseDbCache._validate_case
                referenced_case = case_db.get(index.referenced_id)
                if not referenced_case:
                    # just log, don't raise an error or modify the index
                    logging.error(
                        "Case '%s' references non-existent case '%s'",
                        case.case_id,
                        index.referenced_id,
                    )

    def _get_dirtiness_flags_for_outgoing_indices(case, tree_owners=None):
        """ if the outgoing indices touch cases owned by another user this cases owner is dirty """
        if tree_owners is None:
            tree_owners = set()

        extension_indices = [index for index in case.indices if index.relationship == CASE_INDEX_EXTENSION]

        unowned_host_cases = []
        for index in extension_indices:
            host_case = case_db.get(index.referenced_id)
            if (
                host_case
                and host_case.owner_id == UNOWNED_EXTENSION_OWNER_ID
                and unowned_host_cases not in host_case
            ):
                unowned_host_cases.append(host_case)

        owner_ids = {case_db.get(index.referenced_id).owner_id
                     for index in case.indices if case_db.get(index.referenced_id)} | tree_owners
        potential_clean_owner_ids = owner_ids | set([UNOWNED_EXTENSION_OWNER_ID])
        more_than_one_owner_touched = len(owner_ids) > 1
        touches_different_owner = len(owner_ids) == 1 and case.owner_id not in potential_clean_owner_ids

        if (more_than_one_owner_touched or touches_different_owner):
            yield DirtinessFlag(case.case_id, case.owner_id)
            if extension_indices:
                # If this case is an extension, each of the touched cases is also dirty
                for index in case.indices:
                    referenced_case = case_db.get(index.referenced_id)
                    yield DirtinessFlag(referenced_case.case_id, referenced_case.owner_id)

        if case.owner_id != UNOWNED_EXTENSION_OWNER_ID:
            tree_owners.add(case.owner_id)
        for unowned_host_case in unowned_host_cases:
            # A host case of this extension is unowned, which means it could potentially touch an owned case
            # Check these unowned cases' outgoing indices and mark dirty if appropriate
            for dirtiness_flag in _get_dirtiness_flags_for_outgoing_indices(unowned_host_case,
                                                                            tree_owners=tree_owners):
                yield dirtiness_flag

    def _get_dirtiness_flags_for_child_cases(cases):
        child_cases = case_db.get_reverse_indexed_cases([c.case_id for c in cases])
        case_owner_map = dict((case.case_id, case.owner_id) for case in cases)
        for child_case in child_cases:
            for index in child_case.indices:
                if (index.referenced_id in case_owner_map
                        and child_case.owner_id != case_owner_map[index.referenced_id]):
                    yield DirtinessFlag(child_case.case_id, child_case.owner_id)

    def _get_dirtiness_flags_for_reassigned_case(case_metas):
        # for reassigned cases, we mark them temporarily dirty to allow phones to sync
        # the latest changes. these will get cleaned up when the weekly rebuild triggers
        for case_update_meta in case_metas:
            if _is_change_of_ownership(case_update_meta.previous_owner_id, case_update_meta.case.owner_id):
                yield DirtinessFlag(case_update_meta.case.case_id, case_update_meta.previous_owner_id)

    dirtiness_flags = []
    extensions_to_close = set()

    # process the temporary dirtiness flags first so that any hints for real dirtiness get overridden
    dirtiness_flags += list(_get_dirtiness_flags_for_reassigned_case(touched_cases.values()))
    for case_update_meta in touched_cases.values():
        _validate_indices(case_update_meta.case)
        extensions_to_close = extensions_to_close | get_extensions_to_close(case_update_meta.case, domain)
        dirtiness_flags += list(_get_dirtiness_flags_for_outgoing_indices(case_update_meta.case))
    dirtiness_flags += list(_get_dirtiness_flags_for_child_cases([meta.case for meta in touched_cases.values()]))

    return CaseProcessingResult(
        domain,
        [update.case for update in touched_cases.values()],
        dirtiness_flags,
        extensions_to_close
    )
dataset/ETHPy150Open dimagi/commcare-hq/corehq/ex-submodules/casexml/apps/case/xform.py/_get_or_update_cases/swapped operands of `not in`