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 CaseDbCache(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 test_get_case_types_caching(self): self._send_case_to_es(case_type='t1') self.assertEqual({'t1'}, get_case_types_for_domain_es(self.domain)) self._send_case_to_es(case_type='t2') # cached response self.assertEqual({'t1'}, get_case_types_for_domain_es(self.domain)) # simulate a save from casexml.apps.case.signals import case_post_save case_post_save.send(self, case=CommCareCase(domain=self.domain, type='t2')) self.assertEqual({'t1', 't2'}, get_case_types_for_domain_es(self.domain))
def save_processed_models(self, xforms, case_stock_result): from casexml.apps.case.signals import case_post_save instance = xforms[0] with unfinished_submission(instance) as unfinished_submission_stub: self.interface.save_processed_models( xforms, case_stock_result.case_models, case_stock_result.stock_result ) unfinished_submission_stub.saved = True unfinished_submission_stub.save() case_stock_result.case_result.commit_dirtiness_flags() case_stock_result.stock_result.finalize() for case in case_stock_result.case_models: case_post_save.send(case.__class__, case=case) case_stock_result.case_result.close_extensions()
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. """ assert getattr(settings, 'UNIT_TESTING', False) domain = get_and_check_xform_domain(xform) with CaseDbCache(domain=domain, lock=True, deleted_ok=True) as case_db: cases = process_cases_with_casedb(xform, case_db, config=config) 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) return 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 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 save(self, **params): self.server_modified_on = datetime.utcnow() super(CommCareCase, self).save(**params) case_post_save.send(CommCareCase, case=self)
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