def process_xforms_for_cases(xforms, case_db, timing_context=None): from casexml.apps.case.xform import process_cases_with_casedb from corehq.apps.commtrack.processing import process_stock timing_context = timing_context or TimingContext() instance = xforms[0] with timing_context("process_cases"): case_result = process_cases_with_casedb(xforms, case_db) with timing_context("process_ledgers"): stock_result = process_stock(xforms, case_db) stock_result.populate_models() modified_on_date = instance.received_on if getattr(instance, 'edited_on', None) and instance.edited_on > instance.received_on: modified_on_date = instance.edited_on with timing_context("check_cases_before_save"): cases = case_db.get_cases_for_saving(modified_on_date) return CaseStockProcessingResult( case_result=case_result, case_models=cases, stock_result=stock_result, )
def process_form_unarchived(self, form): from corehq.apps.commtrack.processing import process_stock from casexml.apps.case.models import CommCareCase result = process_stock([form]) result.populate_models() result.commit() result.finalize() CommCareCase.get_db().bulk_save(result.relevant_cases)
def create_form_with_duplicate_stock_transaction(self): from corehq.apps.commtrack.processing import process_stock product = self.make_product() form = self.update_stock(product) stock_result = process_stock([form]) # create duplicate transaction stock_result.populate_models() for model in stock_result.models_to_save: model.save() return product._id
def reprocess_form(sender, xform, *args, **kwargs): from corehq.apps.commtrack.processing import process_stock result = process_stock([xform]) for to_save in result.get_models_to_save(): if to_save: to_save.commit() result.finalize() # todo: use LedgerProcessor CommCareCase.get_db().bulk_save(result.relevant_cases)
def create_form_with_duplicate_stock_transaction(self): from corehq.apps.commtrack.helpers import make_product from corehq.apps.commtrack.processing import process_stock thing1 = make_product(self.domain_name, 'thing-1', 'thing-1') self.submit_form(LEDGER_FORM.replace("thing-1", thing1._id)) stock_result = process_stock([self._get_form("ledger-form")]) stock_result.populate_models() for model in stock_result.models_to_save: model.save() return thing1._id
def update_case_transactions_for_form(case_cache, live_case_updates, deprecated_case_updates, live_form, deprecated_form): for case_update in live_case_updates + deprecated_case_updates: case_id = case_update.id count, _ = CaseTransaction.objects.partitioned_query(case_id)\ .filter(case_id=case_id, form_id=live_form.form_id).delete() rebuild_transactions = CaseTransaction.objects.partitioned_query( case_id).filter(case_id=case_id, type=CaseTransaction.TYPE_REBUILD_FORM_EDIT) for transaction in rebuild_transactions: if transaction.details.get( 'deprecated_form_id') == deprecated_form.original_form_id: transaction.delete() for case_update in live_case_updates: case_id = case_update.id case = case_cache.get(case_id) SqlCaseUpdateStrategy.add_transaction_for_form(case, case_update, live_form) for case_update in deprecated_case_updates: case_id = case_update.id case = case_cache.get(case_id) SqlCaseUpdateStrategy.add_transaction_for_form(case, case_update, deprecated_form) stock_result = process_stock([live_form, deprecated_form], case_cache) stock_result.populate_models() affected_ledgers = set() affected_cases = set() ledger_transactions = [] for ledger_value in stock_result.models_to_save: affected_ledgers.add(ledger_value.ledger_reference) affected_cases.add(ledger_value.case_id) for transaction in ledger_value.get_tracked_models_to_create( LedgerTransaction): ledger_transactions.append(transaction) if affected_cases: LedgerAccessorSQL.delete_ledger_transactions_for_form( affected_cases, live_form.form_id) for transaction in ledger_transactions: transaction.save() for case in case_cache.cache.values(): affected_cases.add(case.case_id) transactions = case.get_tracked_models_to_create(CaseTransaction) for transaction in transactions: transaction.case = case transaction.save() return affected_cases, affected_ledgers
def process_form_unarchived(self, form): from corehq.apps.commtrack.processing import process_stock result = process_stock([form]) result.populate_models() LedgerAccessorSQL.save_ledger_values(result.models_to_save) for ledger_value in result.models_to_save: publish_ledger_v2_saved(ledger_value) refs_to_rebuild = {ledger_value.ledger_reference for ledger_value in result.models_to_save} for ref in refs_to_rebuild: self.rebuild_ledger_state(**ref._asdict()) result.finalize()
def update_case_transactions_for_form(case_cache, live_case_updates, deprecated_case_updates, live_form, deprecated_form): for case_update in live_case_updates + deprecated_case_updates: case_id = case_update.id count, _ = CaseTransaction.objects.partitioned_query(case_id)\ .filter(case_id=case_id, form_id=live_form.form_id).delete() rebuild_transactions = CaseTransaction.objects.partitioned_query(case_id).filter( case_id=case_id, type=CaseTransaction.TYPE_REBUILD_FORM_EDIT ) for transaction in rebuild_transactions: if transaction.details.get('deprecated_form_id') == deprecated_form.original_form_id: transaction.delete() for case_update in live_case_updates: case_id = case_update.id case = case_cache.get(case_id) SqlCaseUpdateStrategy.add_transaction_for_form(case, case_update, live_form) for case_update in deprecated_case_updates: case_id = case_update.id case = case_cache.get(case_id) SqlCaseUpdateStrategy.add_transaction_for_form(case, case_update, deprecated_form) stock_result = process_stock([live_form, deprecated_form], case_cache) stock_result.populate_models() affected_ledgers = set() affected_cases = set() ledger_transactions = [] for ledger_value in stock_result.models_to_save: affected_ledgers.add(ledger_value.ledger_reference) affected_cases.add(ledger_value.case_id) for transaction in ledger_value.get_tracked_models_to_create(LedgerTransaction): ledger_transactions.append(transaction) if affected_cases: LedgerAccessorSQL.delete_ledger_transactions_for_form(list(affected_cases), live_form.form_id) for transaction in ledger_transactions: transaction.save() for case in case_cache.cache.values(): affected_cases.add(case.case_id) transactions = case.get_tracked_models_to_create(CaseTransaction) for transaction in transactions: transaction.case = case transaction.save() return affected_cases, affected_ledgers
def process_form_unarchived(self, form): from corehq.apps.commtrack.processing import process_stock result = process_stock([form]) result.populate_models() LedgerAccessorSQL.save_ledger_values(result.models_to_save) for ledger_value in result.models_to_save: publish_ledger_v2_saved(ledger_value) refs_to_rebuild = { ledger_value.ledger_reference for ledger_value in result.models_to_save } for ref in refs_to_rebuild: self.rebuild_ledger_state(**ref._asdict()) result.finalize()
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 = FormProcessorInterface(domain).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, )
def process_xforms_for_cases(xforms, case_db): from casexml.apps.case.xform import process_cases_with_casedb from corehq.apps.commtrack.processing import process_stock instance = xforms[0] 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 _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 process_xforms_for_cases(xforms, case_db): from casexml.apps.case.xform import process_cases_with_casedb from corehq.apps.commtrack.processing import process_stock instance = xforms[0] case_result = process_cases_with_casedb(xforms, case_db) stock_result = process_stock(xforms, case_db) modified_on_date = instance.received_on if getattr(instance, 'edited_on', None) and instance.edited_on > instance.received_on: modified_on_date = instance.edited_on cases = case_db.get_cases_for_saving(modified_on_date) 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 _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 = FormProcessorInterface(domain).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) 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 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): 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 reprocess_form(sender, xform, *args, **kwargs): from corehq.apps.commtrack.processing import process_stock for case in process_stock(xform): case.save()
def process_stock(cls, xforms, case_db): from corehq.apps.commtrack.processing import process_stock return process_stock(xforms, case_db)
def reprocess_form(sender, xform, *args, **kwargs): from corehq.apps.commtrack.processing import process_stock result = process_stock(xform) result.commit() CommCareCase.get_db().bulk_save(result.relevant_cases)
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()