def _process_cases(xform, config=None): """ Creates or updates case objects which live outside of the form. If reconcile is true it will perform an additional step of reconciling the case update history after the case is processed. """ warnings.warn( 'This function is deprecated. You should be using SubmissionPost.', DeprecationWarning, ) assert getattr(settings, 'UNIT_TESTING', False) domain = get_and_check_xform_domain(xform) with CaseDbCacheCouch(domain=domain, lock=True, deleted_ok=True) as case_db: case_result = process_cases_with_casedb([xform], case_db, config=config) cases = case_result.cases docs = [xform] + cases now = datetime.datetime.utcnow() for case in cases: case.server_modified_on = now
def _process_cases(xform, config=None): """ Creates or updates case objects which live outside of the form. If reconcile is true it will perform an additional step of reconciling the case update history after the case is processed. """ warnings.warn("This function is deprecated. You should be using SubmissionPost.", DeprecationWarning) assert getattr(settings, "UNIT_TESTING", False) domain = get_and_check_xform_domain(xform) with CaseDbCacheCouch(domain=domain, lock=True, deleted_ok=True) as case_db: case_result = process_cases_with_casedb([xform], case_db, config=config) cases = case_result.cases docs = [xform] + cases now = datetime.datetime.utcnow() for case in cases: case.server_modified_on = now XFormInstance.get_db().bulk_save(docs) for case in cases: case_post_save.send(CommCareCase, case=case) case_result.commit_dirtiness_flags() return cases
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 casexml.apps.case.xform import get_and_check_xform_domain from corehq.apps.commtrack.processing import process_stock interface = FormProcessorInterface(domain) get_and_check_xform_domain(sql_form) xforms = [sql_form] # todo: I think this can be changed to lock=False with interface.casedb_cache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: touched_cases = FormProcessorInterface(domain).get_cases_from_forms( case_db, xforms) extensions_to_close = get_all_extensions_to_close( domain, touched_cases.values()) case_result = CaseProcessingResult( domain, [update.case for update in touched_cases.values()], [], # ignore dirtiness_flags, extensions_to_close) # todo: is this necessary? for case in case_result.cases: case_db.mark_changed(case) stock_result = process_stock(xforms, case_db) cases = case_db.get_cases_for_saving(sql_form.received_on) stock_result.populate_models() return CaseStockProcessingResult( case_result=case_result, case_models=cases, stock_result=stock_result, )
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 casexml.apps.case.xform import get_and_check_xform_domain from corehq.apps.commtrack.processing import process_stock interface = FormProcessorInterface(domain) get_and_check_xform_domain(sql_form) xforms = [sql_form] with interface.casedb_cache(domain=domain, lock=False, deleted_ok=True, xforms=xforms) as case_db: touched_cases = FormProcessorInterface(domain).get_cases_from_forms(case_db, xforms) extensions_to_close = get_all_extensions_to_close(domain, 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.mark_changed(case) stock_result = process_stock(xforms, case_db) cases = case_db.get_cases_for_saving(sql_form.received_on) stock_result.populate_models() return CaseStockProcessingResult( case_result=case_result, case_models=cases, stock_result=stock_result, )
def process_xforms_for_cases(self, xforms): from casexml.apps.case.xform import get_and_check_xform_domain from casexml.apps.case.xform import process_cases_with_casedb from corehq.apps.commtrack.processing import process_stock instance = xforms[0] domain = get_and_check_xform_domain(instance) with self.interface.casedb_cache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: case_result = process_cases_with_casedb(xforms, case_db) stock_result = process_stock(xforms, case_db) cases = case_db.get_cases_for_saving(instance.received_on) stock_result.populate_models() return CaseStockProcessingResult( case_result=case_result, case_models=cases, stock_result=stock_result, )
def process_xforms_for_cases(self, xforms): from casexml.apps.case.xform import get_and_check_xform_domain from casexml.apps.case.xform import process_cases_with_casedb from corehq.apps.commtrack.processing import process_stock instance = xforms[0] domain = get_and_check_xform_domain(instance) with self.interface.casedb_cache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: case_result = process_cases_with_casedb(xforms, case_db) stock_result = process_stock(xforms, case_db) cases = case_db.get_cases_for_saving(instance.received_on) stock_result.populate_models() return CaseStockProcessingResult( case_result=case_result, case_models=cases, stock_result=stock_result, )
def run(self): if not self.auth_context.is_valid(): return self.failed_auth_response, None, [] if isinstance(self.instance, const.BadRequest): return HttpResponseBadRequest(self.instance.message), None, [] def process(xform): self._attach_shared_props(xform) scrub_meta(xform) try: lock_manager = process_xform(self.instance, attachments=self.attachments, process=process, domain=self.domain) except SubmissionError as e: logging.exception( u"Problem receiving submission to %s. %s" % ( self.path, unicode(e), ) ) return self.get_exception_response(e.error_log), None, [] else: from casexml.apps.case.models import CommCareCase from casexml.apps.case.xform import ( get_and_check_xform_domain, CaseDbCache, process_cases_with_casedb ) from casexml.apps.case.signals import case_post_save from casexml.apps.case.exceptions import IllegalCaseId, UsesReferrals from corehq.apps.commtrack.processing import process_stock cases = [] responses = [] errors = [] with lock_manager as xforms: instance = xforms[0] if instance.doc_type == 'XFormInstance': if len(xforms) > 1: assert len(xforms) == 2 assert is_deprecation(xforms[1]) domain = get_and_check_xform_domain(instance) with CaseDbCache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: try: case_result = process_cases_with_casedb(xforms, case_db) process_stock(instance, case_db) except (IllegalCaseId, UsesReferrals) as e: # errors we know about related to the content of the form # log the error and respond with a success code so that the phone doesn't # keep trying to send the form instance = _handle_known_error(e, instance) response = self._get_open_rosa_response(instance, None, None) return response, instance, cases except Exception as e: # handle / log the error and reraise so the phone knows to resubmit _handle_unexpected_error(e, instance) raise now = datetime.datetime.utcnow() unfinished_submission_stub = UnfinishedSubmissionStub( xform_id=instance.get_id, timestamp=now, saved=False, domain=domain, ) unfinished_submission_stub.save() cases = case_db.get_changed() # todo: this property is useless now instance.initial_processing_complete = True assert XFormInstance.get_db().uri == CommCareCase.get_db().uri docs = xforms + cases # in saving the cases, we have to do all the things # done in CommCareCase.save() for case in cases: case.initial_processing_complete = True case.server_modified_on = now try: rev = CommCareCase.get_db().get_rev(case.case_id) except ResourceNotFound: pass else: assert rev == case.get_rev, ( "Aborting because there would have been " "a document update conflict. {} {} {}".format( case.get_id, case.get_rev, rev ) ) try: # save both the forms and cases XFormInstance.get_db().bulk_save(docs) except BulkSaveError as e: logging.error('BulkSaveError saving forms', exc_info=1, extra={'details': {'errors': e.errors}}) raise unfinished_submission_stub.saved = True unfinished_submission_stub.save() for case in cases: case_post_save.send(CommCareCase, case=case) case_result.commit_dirtiness_flags() responses, errors = self.process_signals(instance) if errors: # .problems was added to instance instance.save() unfinished_submission_stub.delete() elif instance.doc_type == 'XFormDuplicate': assert len(xforms) == 1 instance.save() response = self._get_open_rosa_response(instance, responses, errors) return response, instance, cases
def run(self): failure_response = self._handle_basic_failure_modes() if failure_response: return FormProcessingResult(failure_response, None, [], [], 'known_failures') result = process_xform_xml(self.domain, self.instance, self.attachments) submitted_form = result.submitted_form self._post_process_form(submitted_form) self._invalidate_caches(submitted_form) submission_type = None if submitted_form.is_submission_error_log: self.formdb.save_new_form(submitted_form) response = self.get_exception_response_and_log(submitted_form, self.path) return FormProcessingResult(response, None, [], [], 'submission_error_log') cases = [] ledgers = [] submission_type = 'unknown' response_nature = error_message = None with result.get_locked_forms() as xforms: from casexml.apps.case.xform import get_and_check_xform_domain domain = get_and_check_xform_domain(xforms[0]) if self.case_db: assert self.case_db.domain == domain case_db_cache = self.case_db case_db_cache.cached_xforms.extend(xforms) else: case_db_cache = self.interface.casedb_cache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) with case_db_cache as case_db: instance = xforms[0] if instance.xmlns == DEVICE_LOG_XMLNS: submission_type = 'device_log' try: process_device_log(self.domain, instance) except Exception: notify_exception(None, "Error processing device log", details={ 'xml': self.instance, 'domain': self.domain }) raise elif instance.is_duplicate: submission_type = 'duplicate' existing_form = xforms[1] stub = UnfinishedSubmissionStub.objects.filter( domain=instance.domain, xform_id=existing_form.form_id ).first() result = None if stub: from corehq.form_processor.reprocess import reprocess_unfinished_stub_with_form result = reprocess_unfinished_stub_with_form(stub, existing_form, lock=False) elif existing_form.is_error: from corehq.form_processor.reprocess import reprocess_form result = reprocess_form(existing_form, lock_form=False) if result and result.error: submission_type = 'error' error_message = result.error if existing_form.is_error: response_nature = ResponseNature.PROCESSING_FAILURE else: response_nature = ResponseNature.POST_PROCESSING_FAILIRE else: self.interface.save_processed_models([instance]) elif not instance.is_error: submission_type = 'normal' try: case_stock_result = self.process_xforms_for_cases(xforms, case_db) except (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError, InvalidCaseIndex, CaseValueError) as e: self._handle_known_error(e, instance, xforms) submission_type = 'error' response_nature = ResponseNature.PROCESSING_FAILURE except Exception as e: # handle / log the error and reraise so the phone knows to resubmit # note that in the case of edit submissions this won't flag the previous # submission as having been edited. this is intentional, since we should treat # this use case as if the edit "failed" handle_unexpected_error(self.interface, instance, e) raise else: instance.initial_processing_complete = True error_message = self.save_processed_models(case_db, xforms, case_stock_result) if error_message: response_nature = ResponseNature.POST_PROCESSING_FAILIRE cases = case_stock_result.case_models ledgers = case_stock_result.stock_result.models_to_save elif instance.is_error: submission_type = 'error' response = self._get_open_rosa_response(instance, error_message, response_nature) return FormProcessingResult(response, instance, cases, ledgers, submission_type)
def run(self): if not self.auth_context.is_valid(): return self.failed_auth_response, None, [] if isinstance(self.instance, const.BadRequest): return HttpResponseBadRequest(self.instance.message), None, [] def process(xform): self._attach_shared_props(xform) scrub_meta(xform) try: lock_manager = process_xform(self.instance, attachments=self.attachments, process=process, domain=self.domain) except SubmissionError as e: logging.exception( u"Problem receiving submission to %s. %s" % ( self.path, unicode(e), ) ) return self.get_exception_response(e.error_log), None, [] else: from casexml.apps.case import process_cases_with_casedb from casexml.apps.case.models import CommCareCase from casexml.apps.case.xform import get_and_check_xform_domain, CaseDbCache from casexml.apps.case.signals import case_post_save from casexml.apps.case.exceptions import IllegalCaseId from corehq.apps.commtrack.processing import process_stock cases = [] responses = [] errors = [] with lock_manager as xforms: instance = xforms[0] if instance.doc_type == "XFormInstance": domain = get_and_check_xform_domain(instance) with CaseDbCache(domain=domain, lock=True, deleted_ok=True) as case_db: try: process_cases_with_casedb(instance, case_db) process_stock(instance, case_db) except IllegalCaseId as e: error_message = '{}: {}'.format( type(e).__name__, unicode(e)) logging.exception(( u"Warning in case or stock processing " u"for form {}: {}." ).format(instance._id, error_message)) instance.__class__ = XFormError instance.problem = error_message instance.save() response = self._get_open_rosa_response(instance, None, None) return response, instance, cases except Exception as e: # Some things to consider here # The following code saves the xform instance # as an XFormError, with a different ID. # That's because if you save with the original ID # and then resubmit, the new submission never has a # chance to get reprocessed; it'll just get saved as # a duplicate. error_message = '{}: {}'.format( type(e).__name__, unicode(e)) new_id = XFormError.get_db().server.next_uuid() logging.exception(( u"Error in case or stock processing " u"for form {}: {}.\n" u"Error saved as {}" ).format(instance._id, error_message, new_id)) instance.__class__ = XFormError instance.orig_id = instance._id instance._id = new_id instance.problem = error_message instance.save() raise now = datetime.datetime.utcnow() unfinished_submission_stub = UnfinishedSubmissionStub( xform_id=instance.get_id, timestamp=now, saved=False, domain=domain, ) unfinished_submission_stub.save() cases = case_db.get_changed() # todo: this property is useless now instance.initial_processing_complete = True assert XFormInstance.get_db().uri == CommCareCase.get_db().uri docs = xforms + cases # in saving the cases, we have to do all the things # done in CommCareCase.save() for case in cases: case.initial_processing_complete = True case.server_modified_on = now try: rev = CommCareCase.get_db().get_rev(case.case_id) except ResourceNotFound: pass else: assert rev == case.get_rev, ( "Aborting because there would have been " "a document update conflict. {} {} {}".format( case.get_id, case.get_rev, rev ) ) try: XFormInstance.get_db().bulk_save(docs) except BulkSaveError as e: logging.exception('BulkSaveError saving forms', extra={'errors': e.errors}) raise unfinished_submission_stub.saved = True unfinished_submission_stub.save() for case in cases: case_post_save.send(CommCareCase, case=case) responses, errors = self.process_signals(instance) if errors: # .problems was added to instance instance.save() unfinished_submission_stub.delete() elif instance.doc_type == 'XFormDuplicate': assert len(xforms) == 1 instance.save() response = self._get_open_rosa_response(instance, responses, errors) return response, instance, cases
def run(self): if timezone_migration_in_progress(self.domain): # keep submissions on the phone # until ready to start accepting again return HttpResponse(status=503), None, [] if not self.auth_context.is_valid(): return self.failed_auth_response, None, [] if isinstance(self.instance, BadRequest): return HttpResponseBadRequest(self.instance.message), None, [] def process(xform): self._attach_shared_props(xform) if xform.doc_type != 'SubmissionErrorLog': found_old = scrub_meta(xform) legacy_soft_assert(not found_old, 'Form with old metadata submitted', xform._id) try: lock_manager = process_xform(self.instance, attachments=self.attachments, process=process, domain=self.domain) except SubmissionError as e: logging.exception( u"Problem receiving submission to %s. %s" % ( self.path, unicode(e), ) ) return self.get_exception_response(e.error_log), None, [] else: from casexml.apps.case.models import CommCareCase from casexml.apps.case.xform import ( get_and_check_xform_domain, CaseDbCache, process_cases_with_casedb ) from casexml.apps.case.signals import case_post_save from casexml.apps.case.exceptions import IllegalCaseId, UsesReferrals from corehq.apps.commtrack.processing import process_stock from corehq.apps.commtrack.exceptions import MissingProductId cases = [] responses = [] errors = [] known_errors = (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError) with lock_manager as xforms: instance = xforms[0] if instance.doc_type == 'XFormInstance': if len(xforms) > 1: assert len(xforms) == 2 assert is_deprecation(xforms[1]) domain = get_and_check_xform_domain(instance) with CaseDbCache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: try: case_result = process_cases_with_casedb(xforms, case_db) stock_result = process_stock(xforms, case_db) except known_errors as e: # errors we know about related to the content of the form # log the error and respond with a success code so that the phone doesn't # keep trying to send the form instance = _handle_known_error(e, instance) xforms[0] = instance # this is usually just one document, but if an edit errored we want # to save the deprecated form as well XFormInstance.get_db().bulk_save(xforms) response = self._get_open_rosa_response( instance, None) return response, instance, cases except Exception as e: # handle / log the error and reraise so the phone knows to resubmit # note that in the case of edit submissions this won't flag the previous # submission as having been edited. this is intentional, since we should treat # this use case as if the edit "failed" error_message = u'{}: {}'.format(type(e).__name__, unicode(e)) instance = _handle_unexpected_error(instance, error_message) instance.save() raise now = datetime.datetime.utcnow() unfinished_submission_stub = UnfinishedSubmissionStub( xform_id=instance.get_id, timestamp=now, saved=False, domain=domain, ) unfinished_submission_stub.save() cases = case_db.get_changed() # todo: this property is only used by the MVPFormIndicatorPillow instance.initial_processing_complete = True # in saving the cases, we have to do all the things # done in CommCareCase.save() for case in cases: legacy_soft_assert(case.version == "2.0", "v1.0 case updated", case.case_id) case.initial_processing_complete = True case.server_modified_on = now try: rev = CommCareCase.get_db().get_rev(case.case_id) except ResourceNotFound: pass else: assert rev == case.get_rev, ( "Aborting because there would have been " "a document update conflict. {} {} {}".format( case.get_id, case.get_rev, rev ) ) # verify that these DB's are the same so that we can save them with one call to bulk_save assert XFormInstance.get_db().uri == CommCareCase.get_db().uri docs = xforms + cases try: XFormInstance.get_db().bulk_save(docs) except BulkSaveError as e: logging.error('BulkSaveError saving forms', exc_info=1, extra={'details': {'errors': e.errors}}) raise except Exception as e: docs_being_saved = [doc['_id'] for doc in docs] error_message = u'Unexpected error bulk saving docs {}: {}, doc_ids: {}'.format( type(e).__name__, unicode(e), ', '.join(docs_being_saved) ) instance = _handle_unexpected_error(instance, error_message) instance.save() raise unfinished_submission_stub.saved = True unfinished_submission_stub.save() case_result.commit_dirtiness_flags() stock_result.commit() for case in cases: case_post_save.send(CommCareCase, case=case) errors = self.process_signals(instance) if errors: # .problems was added to instance instance.save() unfinished_submission_stub.delete() elif instance.doc_type == 'XFormDuplicate': assert len(xforms) == 1 instance.save() response = self._get_open_rosa_response(instance, errors) return response, instance, cases
def run(self): failure_response = self._handle_basic_failure_modes() if failure_response: return FormProcessingResult(failure_response, None, [], []) result = process_xform_xml(self.domain, self.instance, self.attachments) submitted_form = result.submitted_form self._post_process_form(submitted_form) self._invalidate_caches(submitted_form.user_id) if submitted_form.is_submission_error_log: self.formdb.save_new_form(submitted_form) response = self.get_exception_response_and_log( submitted_form, self.path) return FormProcessingResult(response, None, [], []) cases = [] ledgers = [] with result.get_locked_forms() as xforms: from casexml.apps.case.xform import get_and_check_xform_domain domain = get_and_check_xform_domain(xforms[0]) if self.case_db: assert self.case_db.domain == domain case_db_cache = self.case_db case_db_cache.cached_xforms.extend(xforms) else: case_db_cache = self.interface.casedb_cache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) with case_db_cache as case_db: instance = xforms[0] if instance.xmlns == DEVICE_LOG_XMLNS: try: process_device_log(self.domain, instance) except Exception: notify_exception(None, "Error processing device log", details={ 'xml': self.instance, 'domain': self.domain }) raise elif instance.is_duplicate: self.interface.save_processed_models([instance]) elif not instance.is_error: try: case_stock_result = self.process_xforms_for_cases( xforms, case_db) except (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError, InvalidCaseIndex) as e: self._handle_known_error(e, instance, xforms) except Exception as e: # handle / log the error and reraise so the phone knows to resubmit # note that in the case of edit submissions this won't flag the previous # submission as having been edited. this is intentional, since we should treat # this use case as if the edit "failed" handle_unexpected_error(self.interface, instance, e) raise else: instance.initial_processing_complete = True self.save_processed_models(xforms, case_stock_result) case_stock_result.case_result.close_extensions(case_db) cases = case_stock_result.case_models ledgers = case_stock_result.stock_result.models_to_save errors = self.process_signals(instance) response = self._get_open_rosa_response(instance, errors) return FormProcessingResult(response, instance, cases, ledgers)
def run(self): if not self.auth_context.is_valid(): return self.failed_auth_response, None, [] if isinstance(self.instance, const.BadRequest): return HttpResponseBadRequest(self.instance.message), None, [] def process(xform): self._attach_shared_props(xform) scrub_meta(xform) try: lock_manager = process_xform(self.instance, attachments=self.attachments, process=process, domain=self.domain) except SubmissionError as e: logging.exception( u"Problem receiving submission to %s. %s" % ( self.path, unicode(e), ) ) return self.get_exception_response(e.error_log), None, [] else: from casexml.apps.case.models import CommCareCase from casexml.apps.case.xform import ( get_and_check_xform_domain, CaseDbCache, process_cases_with_casedb ) from casexml.apps.case.signals import case_post_save from casexml.apps.case.exceptions import IllegalCaseId, UsesReferrals from corehq.apps.commtrack.processing import process_stock from corehq.apps.commtrack.exceptions import MissingProductId cases = [] responses = [] errors = [] known_errors = (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError) with lock_manager as xforms: instance = xforms[0] if instance.doc_type == 'XFormInstance': if len(xforms) > 1: assert len(xforms) == 2 assert is_deprecation(xforms[1]) domain = get_and_check_xform_domain(instance) with CaseDbCache(domain=domain, lock=True, deleted_ok=True, xforms=xforms) as case_db: try: case_result = process_cases_with_casedb(xforms, case_db) stock_result = process_stock(instance, case_db) except known_errors as e: # errors we know about related to the content of the form # log the error and respond with a success code so that the phone doesn't # keep trying to send the form instance = _handle_known_error(e, instance) xforms[0] = instance # this is usually just one document, but if an edit errored we want # to save the deprecated form as well XFormInstance.get_db().bulk_save(xforms) response = self._get_open_rosa_response(instance, None, None) return response, instance, cases except Exception as e: # handle / log the error and reraise so the phone knows to resubmit # note that in the case of edit submissions this won't flag the previous # submission as having been edited. this is intentional, since we should treat # this use case as if the edit "failed" error_message = u'{}: {}'.format(type(e).__name__, unicode(e)) instance = _handle_unexpected_error(instance, error_message) instance.save() raise now = datetime.datetime.utcnow() unfinished_submission_stub = UnfinishedSubmissionStub( xform_id=instance.get_id, timestamp=now, saved=False, domain=domain, ) unfinished_submission_stub.save() cases = case_db.get_changed() # todo: this property is useless now instance.initial_processing_complete = True assert XFormInstance.get_db().uri == CommCareCase.get_db().uri docs = xforms + cases # in saving the cases, we have to do all the things # done in CommCareCase.save() for case in cases: case.initial_processing_complete = True case.server_modified_on = now try: rev = CommCareCase.get_db().get_rev(case.case_id) except ResourceNotFound: pass else: assert rev == case.get_rev, ( "Aborting because there would have been " "a document update conflict. {} {} {}".format( case.get_id, case.get_rev, rev ) ) try: # save both the forms and cases XFormInstance.get_db().bulk_save(docs) except BulkSaveError as e: logging.error('BulkSaveError saving forms', exc_info=1, extra={'details': {'errors': e.errors}}) raise except Exception as e: docs_being_saved = [doc['_id'] for doc in docs] error_message = u'Unexpected error bulk saving docs {}: {}, doc_ids: {}'.format( type(e).__name__, unicode(e), ', '.join(docs_being_saved) ) instance = _handle_unexpected_error(instance, error_message) instance.save() raise unfinished_submission_stub.saved = True unfinished_submission_stub.save() case_result.commit_dirtiness_flags() stock_result.commit() for case in cases: case_post_save.send(CommCareCase, case=case) responses, errors = self.process_signals(instance) if errors: # .problems was added to instance instance.save() unfinished_submission_stub.delete() elif instance.doc_type == 'XFormDuplicate': assert len(xforms) == 1 instance.save()