Пример #1
0
def get_simple_wrapped_form(form_id, case_id=None, metadata=None, save=True):
    from corehq.form_processor.interfaces.processor import FormProcessorInterface

    metadata = metadata or TestFormMetadata()
    xml = get_simple_form_xml(form_id=form_id, metadata=metadata)
    form_json = convert_xform_to_json(xml)
    interface = FormProcessorInterface(domain=metadata.domain)
    wrapped_form = interface.new_xform(form_json)
    wrapped_form.domain = metadata.domain
    wrapped_form.received_on = metadata.received_on
    interface.store_attachments(wrapped_form,
                                [Attachment('form.xml', xml, 'text/xml')])
    if save:
        interface.save_processed_models([wrapped_form])

    return wrapped_form
Пример #2
0
    def __init__(self,
                 instance=None,
                 attachments=None,
                 auth_context=None,
                 domain=None,
                 app_id=None,
                 build_id=None,
                 path=None,
                 location=None,
                 submit_ip=None,
                 openrosa_headers=None,
                 last_sync_token=None,
                 received_on=None,
                 date_header=None,
                 partial_submission=False,
                 case_db=None,
                 force_logs=False):
        assert domain, "'domain' is required"
        assert instance, instance
        assert not isinstance(instance, HttpRequest), instance
        self.domain = domain
        self.app_id = app_id
        self.build_id = build_id
        # get_location has good default
        self.location = location or couchforms.get_location()
        self.received_on = received_on
        self.date_header = date_header
        self.submit_ip = submit_ip
        self.last_sync_token = last_sync_token
        self.openrosa_headers = openrosa_headers or {}
        self.instance = instance
        self.attachments = attachments or {}
        self.auth_context = auth_context or DefaultAuthContext()
        self.path = path
        self.interface = FormProcessorInterface(domain)
        self.formdb = FormAccessors(domain)
        self.partial_submission = partial_submission
        # always None except in the case where a system form is being processed as part of another submission
        # e.g. for closing extension cases
        self.case_db = case_db
        if case_db:
            assert case_db.domain == domain
        self.force_logs = force_logs

        self.is_openrosa_version3 = self.openrosa_headers.get(
            OPENROSA_VERSION_HEADER, '') == OPENROSA_VERSION_3
        self.track_load = form_load_counter("form_submission", domain)
Пример #3
0
def _get_case_and_ledger_updates(domain, sql_form):
    """
    Get a CaseStockProcessingResult with the appropriate cases and ledgers to
    be saved.

    See SubmissionPost.process_xforms_for_cases and methods it calls for the equivalent
    section of the form-processing code.
    """
    from corehq.apps.commtrack.processing import process_stock

    interface = FormProcessorInterface(domain)

    assert sql_form.domain
    xforms = [sql_form]

    with interface.casedb_cache(
            domain=domain,
            lock=False,
            deleted_ok=True,
            xforms=xforms,
            load_src="couchsqlmigration",
    ) as case_db:
        touched_cases = interface.get_cases_from_forms(case_db, xforms)
        extensions_to_close = get_all_extensions_to_close(
            domain, list(touched_cases.values()))
        case_result = CaseProcessingResult(
            domain,
            [update.case for update in touched_cases.values()],
            [],  # ignore dirtiness_flags,
            extensions_to_close)
        for case in case_result.cases:
            case_db.post_process_case(case, sql_form)
            case_db.mark_changed(case)
        cases = case_result.cases

        try:
            stock_result = process_stock(xforms, case_db)
            cases = case_db.get_cases_for_saving(sql_form.received_on)
            stock_result.populate_models()
        except MissingFormXml:
            stock_result = None

    return CaseStockProcessingResult(
        case_result=case_result,
        case_models=cases,
        stock_result=stock_result,
    )
Пример #4
0
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

    """
    interface = FormProcessorInterface(new_doc.domain)
    conflict_id = new_doc.form_id
    existing_doc = FormAccessors(
        new_doc.domain).get_with_attachments(conflict_id)

    existing_md5 = existing_doc.xml_md5()
    new_md5 = new_doc.xml_md5()

    if existing_md5 != new_md5:
        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(to='{}@{}.com'.format('skelly', 'dimagi'),
                        exponential_backoff=False)(
                            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 FormProcessingResult(xform)
        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
            existing_doc, new_doc = apply_deprecation(existing_doc, new_doc,
                                                      interface)
            return FormProcessingResult(new_doc, existing_doc)
    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 FormProcessingResult(duplicate)
Пример #5
0
def _perfom_post_save_actions(form, save=True):
    interface = FormProcessorInterface(form.domain)
    cache = interface.casedb_cache(domain=form.domain,
                                   lock=False,
                                   deleted_ok=True,
                                   xforms=[form])
    with cache as casedb:
        case_stock_result = SubmissionPost.process_xforms_for_cases([form],
                                                                    casedb)
        try:
            save and SubmissionPost.do_post_save_actions(
                casedb, [form], case_stock_result)
        except PostSaveError:
            error_message = "Error performing post save operations"
            return ReprocessingResult(form, None, None, error_message)
        return ReprocessingResult(form, case_stock_result.case_models, None,
                                  None)
Пример #6
0
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)
    _validate_indices(case_db, [case_update_meta.case for case_update_meta in touched_cases.values()])
    dirtiness_flags = _get_all_dirtiness_flags_from_cases(case_db, touched_cases)
    extensions_to_close = get_all_extensions_to_close(domain, touched_cases.values())
    return CaseProcessingResult(
        domain,
        [update.case for update in touched_cases.values()],
        dirtiness_flags,
        extensions_to_close
    )
def _migrate_form(domain, couch_form):
    """
    This copies the couch form into a new sql form but does not save it.

    See form_processor.parsers.form._create_new_xform
    and SubmissionPost._set_submission_properties for what this should do.
    """
    interface = FormProcessorInterface(domain)

    form_data = couch_form.form
    with force_phone_timezones_should_be_processed():
        adjust_datetimes(form_data)
    sql_form = interface.new_xform(form_data)
    sql_form.form_id = couch_form.form_id   # some legacy forms don't have ID's so are assigned random ones
    if sql_form.xmlns is None:
        sql_form.xmlns = ''
    return _copy_form_properties(domain, sql_form, couch_form)
Пример #8
0
    def populate_models(self):
        self.populated = True

        interface = FormProcessorInterface(domain=self.domain)
        processor = interface.ledger_processor
        ledger_db = interface.ledger_db

        for helper in self.stock_report_helpers:
            assert helper.domain == self.domain

        normal_helpers = [srh for srh in self.stock_report_helpers if not srh.deprecated]
        deprecated_helpers = [srh for srh in self.stock_report_helpers if srh.deprecated]

        models_result = processor.get_models_to_update(
            self.xform.form_id, normal_helpers, deprecated_helpers, ledger_db
        )
        self.models_to_save, self.models_to_delete = models_result
Пример #9
0
def _create_new_xform(domain,
                      instance_xml,
                      attachments=None,
                      auth_context=None):
    """
    create but do not save an XFormInstance from an xform payload (xml_string)
    optionally set the doc _id to a predefined value (_id)
    return doc _id of the created doc

    `process` is transformation to apply to the form right before saving
    This is to avoid having to save multiple times

    If xml_string is bad xml
      - raise couchforms.XMLSyntaxError
      :param domain:

    :returns: FormProcessingResult or raises an error
    """
    from corehq.form_processor.interfaces.processor import FormProcessorInterface
    interface = FormProcessorInterface(domain)

    assert attachments is not None
    form_data = convert_xform_to_json(instance_xml)
    if not form_data.get('@xmlns'):
        raise MissingXMLNSError("Form is missing a required field: XMLNS")

    adjust_datetimes(form_data)

    xform = interface.new_xform(form_data)
    xform.domain = domain
    xform.auth_context = auth_context

    # Maps all attachments to uniform format and adds form.xml to list before storing
    attachments = [
        Attachment(name=a[0], raw_content=a[1], content_type=a[1].content_type)
        for a in attachments.items()
    ]
    attachments.append(
        Attachment(name='form.xml',
                   raw_content=instance_xml,
                   content_type='text/xml'))
    interface.store_attachments(xform, attachments)

    assert xform.is_normal
    return FormProcessingResult(xform)
Пример #10
0
    def test_casedb_already_has_cases(self):
        casedb_cache = FormProcessorInterface().casedb_cache
        case = CaseFactory().create_case()
        case_db = casedb_cache(
            initial=[CommCareCase(_id='fake1'),
                     CommCareCase(_id='fake2')])
        form = XFormInstance.get(case.xform_ids[0])

        def assert_exactly_one_case(sender, xform, cases, **kwargs):
            global case_count
            case_count = len(cases)

        cases_received.connect(assert_exactly_one_case)
        try:
            process_cases_with_casedb([form], case_db)
            self.assertEqual(1, case_count)
        finally:
            cases_received.disconnect(assert_exactly_one_case)
Пример #11
0
    def __init__(self, domain=None, strip_history=False, deleted_ok=False,
                 lock=False, wrap=True, initial=None, xforms=None):

        self._populate_from_initial(initial)
        self.domain = domain
        self.cached_xforms = xforms if xforms is not None else []
        self.strip_history = strip_history
        self.deleted_ok = deleted_ok
        self.lock = lock
        self.wrap = wrap
        if self.lock and not self.wrap:
            raise ValueError('Currently locking only supports explicitly wrapping cases!')
        self.locks = []
        self._changed = set()
        # this is used to allow casedb to be re-entrant. Each new context pushes the parent context locks
        # onto this stack and restores them when the context exits
        self.lock_stack = []
        self.processor_interface = FormProcessorInterface(self.domain)
Пример #12
0
    def _count_forms(self, case_id, context):
        domain = context.root_doc['domain']

        xmlns = [self.xmlns] if isinstance(self.xmlns,
                                           six.string_types) else self.xmlns

        cache_key = (self.__class__.__name__, case_id,
                     hashlib.md5(','.join(xmlns)).hexdigest())
        if context.get_cache_value(cache_key) is not None:
            return context.get_cache_value(cache_key)

        xforms = FormProcessorInterface(domain).get_case_forms(case_id)
        count = len([
            form for form in xforms
            if form.xmlns in xmlns and form.domain == domain
        ])
        context.set_cache_value(cache_key, count)
        return count
Пример #13
0
    def test_casedb_already_has_cases(self):
        casedb_cache = FormProcessorInterface().casedb_cache
        case = CaseFactory().create_case()
        case_db = casedb_cache(initial=[
            CommCareCase(case_id='fake1'),
            CommCareCase(case_id='fake2'),
        ])
        form = XFormInstance.objects.get_form(case.xform_ids[0])
        received = []

        def receive_cases(sender, xform, cases, **kwargs):
            received.extend(cases)

        cases_received.connect(receive_cases)
        try:
            process_cases_with_casedb([form], case_db)
            self.assertEqual(len(received), 1)
        finally:
            cases_received.disconnect(receive_cases)
Пример #14
0
def apply_deprecation(existing_xform, new_xform, interface=None):
    # 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

    interface = interface or FormProcessorInterface(existing_xform.domain)
    interface.copy_attachments(existing_xform, new_xform)
    new_xform.form_id = existing_xform.form_id
    existing_xform = interface.assign_new_id(existing_xform)
    existing_xform.orig_id = new_xform.form_id

    # and give the new doc server data of the old one and some metadata
    new_xform.received_on = existing_xform.received_on
    new_xform.deprecated_form_id = existing_xform.form_id
    new_xform.edited_on = datetime.datetime.utcnow()
    existing_xform.edited_on = new_xform.edited_on

    return interface.apply_deprecation(existing_xform, new_xform)
Пример #15
0
    def _get_forms(self, case_id, context):
        domain = context.root_doc['domain']

        cache_key = (self.__class__.__name__, case_id, self.xmlns, self.reverse)
        if context.get_cache_value(cache_key) is not None:
            return context.get_cache_value(cache_key)

        xforms = FormProcessorInterface(domain).get_case_forms(case_id)
        xforms = sorted(
            [form for form in xforms if form.xmlns == self.xmlns and form.domain == domain],
            key=lambda x: x.received_on
        )
        if not xforms:
            form = None
        else:
            index = -1 if self.reverse else 0
            form = xforms[index].to_json()

        context.set_cache_value(cache_key, form)
        return form
Пример #16
0
def _handle_id_conflict(xform, domain):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the
    previous form and overwrite it with the new form's contents.
    """

    assert domain
    conflict_id = xform.form_id

    interface = FormProcessorInterface(domain)
    if interface.is_duplicate(conflict_id, domain):
        # It looks like a duplicate/edit in the same domain so pursue that workflow.
        return _handle_duplicate(xform)
    else:
        # the same form was submitted to two domains, or a form was submitted with
        # an ID that belonged to a different doc type. these are likely developers
        # manually testing or broken API users. just resubmit with a generated ID.
        xform = interface.assign_new_id(xform)
        return FormProcessingResult(xform)
Пример #17
0
def process_stock(xforms, case_db=None):
    """
    process the commtrack xml constructs in an incoming submission
    """
    if not case_db:
        case_db = FormProcessorInterface(xforms[0].domain).casedb_cache(
            domain=xforms[0].domain,
            load_src="process_stock",
        )
    else:
        assert isinstance(case_db, AbstractCaseDbCache)

    stock_report_helpers = []
    case_action_intents = []
    sorted_forms = sorted(xforms, key=lambda f: 1 if f.is_deprecated else 0)
    for xform in sorted_forms:
        try:
            actions_for_form = get_stock_actions(xform)
            stock_report_helpers += actions_for_form.stock_report_helpers
            case_action_intents += actions_for_form.case_action_intents
        except MissingFormXml:
            if not xform.is_deprecated:
                raise

            # in the case where the XML is missing for the deprecated form add a
            # deprecation intent for all cases touched by the primary form
            case_ids = {intent.case_id for intent in case_action_intents}
            case_action_intents += get_ledger_case_action_intents(
                xform, case_ids)

    # validate the parsed transactions
    for stock_report_helper in stock_report_helpers:
        stock_report_helper.validate()

    relevant_cases = mark_cases_changed(case_action_intents, case_db)
    return StockProcessingResult(
        xform=sorted_forms[0],
        relevant_cases=relevant_cases,
        stock_report_helpers=stock_report_helpers,
    )
Пример #18
0
    def handle(self, log_file, **options):
        to_archive = []
        case_id = options.get('case_id')
        case_forms = FormProcessorInterface(domain).get_case_forms(case_id)
        for form in case_forms:
            if form.user_id in ("system", "",
                                None) and form.metadata.username == "system":
                updates = get_case_updates(form)
                if options.get('case_property') is not None:
                    update_actions = [
                        update.get_update_action() for update in updates
                        if update.id == case_id
                    ]
                    for action in update_actions:
                        if isinstance(action, CaseUpdateAction):
                            if options.get('case_property') in set(
                                    action.dynamic_properties.keys()):
                                to_archive.append(form)
                else:
                    to_archive.append(form)

        to_archive.sort(key=lambda f: f.received_on)
        to_archive = to_archive[:-1]

        print("Will archive {} forms".format(len(to_archive)))

        xform_archived.disconnect(rebuild_form_cases)
        with open(log_file, "w") as f:
            for form in with_progress_bar(to_archive):
                f.write(form.form_id + "\n")
                f.flush()
                if options['commit']:
                    form.archive(
                        user_id="archive_single_case_repeater_forms_script")
        xform_archived.connect(rebuild_form_cases)

        rebuild_case_from_forms(
            domain, case_id,
            UserRequestedRebuild(
                user_id="archive_single_case_repeater_forms_script"))
Пример #19
0
 def __init__(self,
              instance=None,
              attachments=None,
              auth_context=None,
              domain=None,
              app_id=None,
              build_id=None,
              path=None,
              location=None,
              submit_ip=None,
              openrosa_headers=None,
              last_sync_token=None,
              received_on=None,
              date_header=None,
              partial_submission=False,
              case_db=None):
     assert domain, domain
     assert instance, instance
     assert not isinstance(instance, HttpRequest), instance
     self.domain = domain
     self.app_id = app_id
     self.build_id = build_id
     # get_location has good default
     self.location = location or couchforms.get_location()
     self.received_on = received_on
     self.date_header = date_header
     self.submit_ip = submit_ip
     self.last_sync_token = last_sync_token
     self.openrosa_headers = openrosa_headers or {}
     self.instance = instance
     self.attachments = attachments or {}
     self.auth_context = auth_context or DefaultAuthContext()
     self.path = path
     self.interface = FormProcessorInterface(domain)
     self.formdb = FormAccessors(domain)
     self.partial_submission = partial_submission
     # always None except in the case where a system form is being processed as part of another submission
     # e.g. for closing extension cases
     self.case_db = case_db
Пример #20
0
    def _get_forms(self, case_id, context):
        domain = context.root_doc['domain']

        xmlns = [self.xmlns] if isinstance(self.xmlns, six.string_types) else self.xmlns

        cache_key = (self.__class__.__name__, case_id, hashlib.md5(','.join(xmlns)).hexdigest(), self.reverse)
        if context.get_cache_value(cache_key) is not None:
            return context.get_cache_value(cache_key)

        xforms = FormProcessorInterface(domain).get_case_forms(case_id)
        xforms = sorted(
            [form for form in xforms if form.xmlns in xmlns and form.domain == domain],
            key=lambda x: x.received_on
        )
        if not xforms:
            form = None
        else:
            index = -1 if self.reverse else 0
            form = xforms[index].to_json()

        context.set_cache_value(cache_key, form)
        return form
Пример #21
0
def _migrate_form_and_attachments(domain, couch_form):
    """
    This copies the couch form into a new sql form but does not save it.

    See form_processor.parsers.form._create_new_xform
    and SubmissionPost._set_submission_properties for what this should do.
    """
    interface = FormProcessorInterface(domain)

    form_data = couch_form.form
    # todo: timezone migration if we want here
    # adjust_datetimes(form_data)
    sql_form = interface.new_xform(form_data)
    assert isinstance(sql_form, XFormInstanceSQL)
    sql_form.domain = domain

    # todo: attachments.
    # note that if these are in the blobdb then we likely don't need to move them,
    # just need to bring the references across
    # interface.store_attachments(xform, attachments)

    # submission properties
    sql_form.auth_context = couch_form.auth_context
    sql_form.submit_ip = couch_form.submit_ip

    # todo: this property appears missing from sql forms - do we need it?
    # sql_form.path = couch_form.path

    sql_form.openrosa_headers = couch_form.openrosa_headers
    sql_form.last_sync_token = couch_form.last_sync_token
    sql_form.received_on = couch_form.received_on
    sql_form.date_header = couch_form.date_header
    sql_form.app_id = couch_form.app_id
    sql_form.build_id = couch_form.build_id
    # export_tag intentionally removed
    # sql_form.export_tag = ["domain", "xmlns"]
    sql_form.partial_submission = couch_form.partial_submission
    return sql_form
Пример #22
0
def get_form_ready_to_save(metadata, is_db_test=False):
    from corehq.form_processor.parsers.form import process_xform_xml
    from corehq.form_processor.utils import get_simple_form_xml, convert_xform_to_json
    from corehq.form_processor.interfaces.processor import FormProcessorInterface
    from corehq.form_processor.models import Attachment

    assert metadata is not None
    metadata.domain = metadata.domain or uuid.uuid4().hex
    form_id = uuid.uuid4().hex
    form_xml = get_simple_form_xml(form_id=form_id, metadata=metadata)

    if is_db_test:
        wrapped_form = process_xform_xml(metadata.domain, form_xml).submitted_form
    else:
        interface = FormProcessorInterface(domain=metadata.domain)
        form_json = convert_xform_to_json(form_xml)
        wrapped_form = interface.new_xform(form_json)
        wrapped_form.domain = metadata.domain
        interface.store_attachments(wrapped_form, [
            Attachment(name='form.xml', raw_content=form_xml, content_type='text/xml')
        ])
    wrapped_form.received_on = metadata.received_on
    wrapped_form.app_id = metadata.app_id
    return wrapped_form
Пример #23
0
def _perfom_post_save_actions(form, save=True):
    interface = FormProcessorInterface(form.domain)
    cache = interface.casedb_cache(
        domain=form.domain, lock=False, deleted_ok=True, xforms=[form]
    )
    with cache as casedb:
        case_stock_result = SubmissionPost.process_xforms_for_cases([form], casedb)
        case_models = case_stock_result.case_models

        if interface.use_sql_domain:
            forms = ProcessedForms(form, None)
            stock_result = case_stock_result.stock_result
            try:
                FormProcessorSQL.publish_changes_to_kafka(forms, case_models, stock_result)
            except Exception:
                error_message = "Error publishing to kafka"
                return ReprocessingResult(form, None, None, error_message)

        try:
            save and SubmissionPost.do_post_save_actions(casedb, [form], case_stock_result)
        except PostSaveError:
            error_message = "Error performing post save operations"
            return ReprocessingResult(form, None, None, error_message)
        return ReprocessingResult(form, case_models, None, None)
Пример #24
0
 def __init__(self, submitted_form):
     self.submitted_form = submitted_form
     self.interface = FormProcessorInterface(self.submitted_form.domain)
Пример #25
0
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
    try:
        existing_doc = FormAccessors(new_doc.domain).get_with_attachments(conflict_id)
    except ResourceNotFound:
        # Original form processing failed but left behind a form doc with no
        # attachments. It's safe to delete this now since we're going to re-process
        # the form anyway.
        from couchforms.models import XFormInstance
        XFormInstance.get_db().delete_doc(conflict_id)
        return new_doc, None

    try:
        existing_md5 = existing_doc.xml_md5()
    except MissingFormXml:
        existing_md5 = None
        if not existing_doc.is_error:
            existing_doc.problem = 'Missing form.xml'

    new_md5 = new_doc.xml_md5()

    if existing_md5 != new_md5:
        _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 and not existing_doc.initial_processing_complete:
                # 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

                # 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
                if not interface.use_sql_domain:
                    new_doc._rev, existing_doc._rev = existing_doc._rev, new_doc._rev
                interface.assign_new_id(existing_doc)
                existing_doc.save()
                return new_doc, None
            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
                NotAllowed.check(new_doc.domain)
                existing_doc, new_doc = apply_deprecation(existing_doc, new_doc, interface)
                return new_doc, existing_doc
    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
Пример #26
0
 def setUpClass(cls):
     super(StrictDatetimesTest, cls).setUpClass()
     cls.domain = 'strict-datetimes-test-domain'
     cls.interface = FormProcessorInterface(cls.domain)
Пример #27
0
def reprocess_form(sender, xform, *args, **kwargs):
    from corehq.form_processor.interfaces.processor import FormProcessorInterface
    FormProcessorInterface(xform.domain).ledger_processor.process_form_unarchived(xform)
Пример #28
0
    def test_update_responses(self):
        formxml = FormSubmissionBuilder(
            form_id='123',
            form_properties={
                'breakfast': 'toast',   # Simple questions
                'lunch': 'sandwich',
                'cell': {               # Simple group
                    'cytoplasm': 'squishy',
                    'organelles': 'grainy',
                },
                'shelves': [            # Simple repeat group
                    {'position': 'top'},
                    {'position': 'middle'},
                    {'position': 'bottom'},
                ],
                'grandparent': [        # Repeat group with child group
                    {'name': 'Haruki'},
                    {'name': 'Sugako'},
                    {
                        'name': 'Emma',
                        'parent': {
                            'name': 'Haruki',
                            'child': {
                                'name': 'Nao',
                            },
                        }
                    },
                ],
                'body': [               # Repeat group with child repeat group
                    {'arm': [
                        {'elbow': '1'},
                        {'finger': '5'},
                    ]},
                    {'leg': [
                        {'knee': '1'},
                        {'toe': '5'},
                    ]},
                ],
            }
        ).as_xml_string()
        pic = UploadedFile(BytesIO(b"fake"),
                           'pic.jpg',
                           content_type='image/jpeg')
        xform = submit_form_locally(formxml,
                                    DOMAIN,
                                    attachments={
                                        "image": pic
                                    }).xform

        updates = {
            'breakfast': 'fruit',
            'cell/organelles': 'bulbous',
            'shelves[1]/position': 'third',
            'shelves[3]/position': 'first',
            'grandparent[1]/name': 'Haruki #1',
            'grandparent[3]/name': 'Ema',
            'grandparent[3]/parent/name': 'Haruki #2',
            'grandparent[3]/parent/child/name': 'Nao-chan',
            'body[1]/arm[1]/elbow': '2',
            'body[2]/leg[2]/toe': '10',
        }
        errors = FormProcessorInterface(DOMAIN).update_responses(
            xform, updates, 'user1')
        form = FormAccessors(DOMAIN).get_form(xform.form_id)
        self.assertEqual(0, len(errors))
        self.assertEqual('fruit', form.form_data['breakfast'])
        self.assertEqual('sandwich', form.form_data['lunch'])
        self.assertEqual('squishy', form.form_data['cell']['cytoplasm'])
        self.assertEqual('bulbous', form.form_data['cell']['organelles'])
        self.assertEqual('third', form.form_data['shelves'][0]['position'])
        self.assertEqual('middle', form.form_data['shelves'][1]['position'])
        self.assertEqual('first', form.form_data['shelves'][2]['position'])
        self.assertEqual('Haruki #1', form.form_data['grandparent'][0]['name'])
        self.assertEqual('Sugako', form.form_data['grandparent'][1]['name'])
        self.assertEqual('Ema', form.form_data['grandparent'][2]['name'])
        self.assertEqual('Haruki #2',
                         form.form_data['grandparent'][2]['parent']['name'])
        self.assertEqual(
            'Nao-chan',
            form.form_data['grandparent'][2]['parent']['child']['name'])
        self.assertEqual('2', form.form_data['body'][0]['arm'][0]['elbow'])
        self.assertEqual('5', form.form_data['body'][0]['arm'][1]['finger'])
        self.assertEqual('1', form.form_data['body'][1]['leg'][0]['knee'])
        self.assertEqual('10', form.form_data['body'][1]['leg'][1]['toe'])
        self.assertIn("image", form.attachments)
        self.assertEqual(form.get_attachment("image"), b"fake")
Пример #29
0
 def _create_form(self, domain):
     metadata = TestFormMetadata(domain=domain)
     form = get_form_ready_to_save(metadata)
     FormProcessorInterface(domain=domain).save_processed_models([form])
     return form
Пример #30
0
 def setUp(self):
     self.interface = FormProcessorInterface()