(new_doc)
Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain
    and *must* include inline attachments

    :returns: A two-tuple: `(<new form>, <duplicate form or None>)`
    The new form may have a different `form_id` than `new_doc.form_id`.
Handle duplicate xforms and xform editing ('deprecation')
Handle duplicate xforms and xform editing ( deprecation )
def _handle_duplicate(new_doc):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain
    and *must* include inline attachments

    :returns: A two-tuple: `(<new form>, <duplicate form or None>)`
    The new form may have a different `form_id` than `new_doc.form_id`.
    """
    interface = FormProcessorInterface(new_doc.domain)
    conflict_id = new_doc.form_id
    existing_doc = FormAccessors(new_doc.domain).get_with_attachments(conflict_id)

    is_icds = settings.SERVER_ENVIRONMENT in settings.ICDS_ENVS
    try:
        if is_icds and new_doc.metadata.deviceID == existing_doc.metadata.deviceID:
            # ICDS does not use 'edit form' functionality via the web and form editing is not possible
            # on mobile devices so it's safe to assume this is a duplicate without checking md5 etc.
            duplicate = interface.deduplicate_xform(new_doc)
            return duplicate, existing_doc

        existing_md5 = existing_doc.xml_md5()
    except MissingFormXml:
        existing_md5 = new_md5 = None
        if not existing_doc.is_error:
            existing_doc.problem = 'Missing form.xml'
    else:
        # only do this if we need to do the comparison
        new_md5 = new_doc.xml_md5()

    if existing_md5 is None or existing_md5 != new_md5:

        def _deprecate_old_form():
            existing, new = apply_deprecation(existing_doc, new_doc, interface)
            return new, existing

        def _replace_old_form():
            interface.assign_new_id(existing_doc)
            existing_doc.orig_id = new_doc.form_id
            existing_doc.save()
            return new_doc, None

        _soft_assert = soft_assert(to='{}@{}.com'.format('skelly', 'dimagi'), exponential_backoff=False)
        if new_doc.xmlns != existing_doc.xmlns:
            # if the XMLNS has changed this probably isn't a form edit
            # it could be a UUID clash (yes we've had that before)
            # Assign a new ID to the form and process as normal + notify_admins
            xform = interface.assign_new_id(new_doc)
            _soft_assert(
                False, "Potential UUID clash", {
                    'incoming_form_id': conflict_id,
                    'existing_form_id': existing_doc.form_id,
                    'new_form_id': xform.form_id,
                    'incoming_xmlns': new_doc.xmlns,
                    'existing_xmlns': existing_doc.xmlns,
                    'domain': new_doc.domain,
                }
            )
            return xform, None
        else:
            if existing_doc.is_error:
                # edge case from ICDS where a form errors and then future re-submissions of the same
                # form do not have the same MD5 hash due to a bug on mobile:
                # see https://dimagi-dev.atlassian.net/browse/ICDS-376
                if not existing_doc.initial_processing_complete:
                    # since we have a new form and the old one was not successfully processed
                    # we can effectively ignore this form and process the new one as normal
                    return _replace_old_form()
                elif not interface.form_has_case_transactions(existing_doc.form_id):
                    # likely an error during saving
                    return _replace_old_form()
                else:
                    return _deprecate_old_form()
            else:
                # if the form contents are not the same:
                #  - "Deprecate" the old form by making a new document with the same contents
                #    but a different ID and a doc_type of XFormDeprecated
                #  - Save the new instance to the previous document to preserve the ID
                return _deprecate_old_form()
    else:
        # follow standard dupe handling, which simply saves a copy of the form
        # but a new doc_id, and a doc_type of XFormDuplicate
        duplicate = interface.deduplicate_xform(new_doc)
        return duplicate, existing_doc
def _handle_duplicate ( new_doc ) : interface = FormProcessorInterface ( new_doc . domain ) conflict_id = new_doc . form_id existing_doc = FormAccessors ( new_doc . domain ) . get_with_attachments ( conflict_id ) is_icds = settings . SERVER_ENVIRONMENT in settings . ICDS_ENVS try : if is_icds and new_doc . metadata . deviceID == existing_doc . metadata . deviceID : # ICDS does not use 'edit form' functionality via the web and form editing is not possible # on mobile devices so it's safe to assume this is a duplicate without checking md5 etc. duplicate = interface . deduplicate_xform ( new_doc ) return duplicate , existing_doc existing_md5 = existing_doc . xml_md5 ( ) except MissingFormXml : existing_md5 = new_md5 = None if not existing_doc . is_error : existing_doc . problem = 'Missing form.xml' else : # only do this if we need to do the comparison new_md5 = new_doc . xml_md5 ( ) if existing_md5 is None or existing_md5 != new_md5 : def _deprecate_old_form ( ) : existing , new = apply_deprecation ( existing_doc , new_doc , interface ) return new , existing def _replace_old_form ( ) : interface . assign_new_id ( existing_doc ) existing_doc . orig_id = new_doc . form_id existing_doc . save ( ) return new_doc , None _soft_assert = soft_assert ( to = '{}@{}.com' . format ( 'skelly' , 'dimagi' ) , exponential_backoff = False ) if new_doc . xmlns != existing_doc . xmlns : # if the XMLNS has changed this probably isn't a form edit # it could be a UUID clash (yes we've had that before) # Assign a new ID to the form and process as normal + notify_admins xform = interface . assign_new_id ( new_doc ) _soft_assert ( False , "Potential UUID clash" , { 'incoming_form_id' : conflict_id , 'existing_form_id' : existing_doc . form_id , 'new_form_id' : xform . form_id , 'incoming_xmlns' : new_doc . xmlns , 'existing_xmlns' : existing_doc . xmlns , 'domain' : new_doc . domain , } ) return xform , None else : if existing_doc . is_error : # edge case from ICDS where a form errors and then future re-submissions of the same # form do not have the same MD5 hash due to a bug on mobile: # see https://dimagi-dev.atlassian.net/browse/ICDS-376 if not existing_doc . initial_processing_complete : # since we have a new form and the old one was not successfully processed # we can effectively ignore this form and process the new one as normal return _replace_old_form ( ) elif not interface . form_has_case_transactions ( existing_doc . form_id ) : # likely an error during saving return _replace_old_form ( ) else : return _deprecate_old_form ( ) else : # if the form contents are not the same: #  - "Deprecate" the old form by making a new document with the same contents #    but a different ID and a doc_type of XFormDeprecated #  - Save the new instance to the previous document to preserve the ID return _deprecate_old_form ( ) else : # follow standard dupe handling, which simply saves a copy of the form # but a new doc_id, and a doc_type of XFormDuplicate duplicate = interface . deduplicate_xform ( new_doc ) return duplicate , existing_doc