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' 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.is_openrosa_version3 = self.openrosa_headers.get(OPENROSA_VERSION_HEADER, '') == OPENROSA_VERSION_3 self.track_load = form_load_counter("form_submission", domain)
def hard_rebuild_case(domain, case_id, detail, save=True, lock=True): if lock: # only record metric if locking since otherwise it has been # (most likley) recorded elsewhere case_load_counter("rebuild_case", domain)() case, lock_obj = FormProcessorCouch.get_case_with_lock(case_id, lock=lock, wrap=True) found = bool(case) if not found: case = CommCareCase() case.case_id = case_id case.domain = domain if lock: lock_obj = CommCareCase.get_obj_lock_by_id(case_id) acquire_lock(lock_obj, degrade_gracefully=False) try: assert case.domain == domain, (case.domain, domain) forms = FormProcessorCouch.get_case_forms(case_id) form_load_counter("rebuild_case", domain)(len(forms)) filtered_forms = [f for f in forms if f.is_normal] sorted_forms = sorted(filtered_forms, key=lambda f: f.received_on) actions = _get_actions_from_forms(domain, sorted_forms, case_id) if not found and case.domain is None: case.domain = domain rebuild_case_from_actions(case, actions) # todo: should this move to case.rebuild? if not case.xform_ids: if not found: return None # there were no more forms. 'delete' the case case.doc_type = 'CommCareCase-Deleted' # add a "rebuild" action case.actions.append(_rebuild_action()) if save: case.save() return case finally: release_lock(lock_obj, degrade_gracefully=True)
def reprocess_form(form, save=True, lock_form=True): if lock_form: # track load if locking; otherise it will be tracked elsewhere form_load_counter("reprocess_form", form.domain)() interface = FormProcessorInterface(form.domain) lock = interface.acquire_lock_for_xform(form.form_id) if lock_form else None with LockManager(form, lock): logger.info('Reprocessing form: %s (%s)', form.form_id, form.domain) # reset form state prior to processing if should_use_sql_backend(form.domain): form.state = XFormInstanceSQL.NORMAL else: form.doc_type = 'XFormInstance' cache = interface.casedb_cache( domain=form.domain, lock=True, deleted_ok=True, xforms=[form], load_src="reprocess_form", ) with cache as casedb: try: case_stock_result = SubmissionPost.process_xforms_for_cases([form], casedb) except (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError, InvalidCaseIndex, CaseValueError) as e: error_message = '{}: {}'.format(type(e).__name__, six.text_type(e)) form = interface.xformerror_from_xform_instance(form, error_message) return ReprocessingResult(form, [], [], error_message) form.initial_processing_complete = True form.problem = None stock_result = case_stock_result.stock_result assert stock_result.populated cases = case_stock_result.case_models _log_changes(cases, stock_result.models_to_save, stock_result.models_to_delete) ledgers = [] if should_use_sql_backend(form.domain): cases_needing_rebuild = _get_case_ids_needing_rebuild(form, cases) ledgers = stock_result.models_to_save ledgers_updated = {ledger.ledger_reference for ledger in ledgers if ledger.is_saved()} if save: for case in cases: CaseAccessorSQL.save_case(case) LedgerAccessorSQL.save_ledger_values(ledgers) FormAccessorSQL.update_form_problem_and_state(form) FormProcessorSQL.publish_changes_to_kafka(ProcessedForms(form, None), cases, stock_result) # rebuild cases and ledgers that were affected for case in cases: if case.case_id in cases_needing_rebuild: logger.info('Rebuilding case: %s', case.case_id) if save: # only rebuild cases that were updated detail = FormReprocessRebuild(form_id=form.form_id) interface.hard_rebuild_case(case.case_id, detail, lock=False) for ledger in ledgers: if ledger.ledger_reference in ledgers_updated: logger.info('Rebuilding ledger: %s', ledger.ledger_reference) if save: # only rebuild updated ledgers interface.ledger_processor.rebuild_ledger_state(**ledger.ledger_reference._asdict()) else: if save: interface.processor.save_processed_models([form], cases, stock_result) from casexml.apps.stock.models import StockTransaction ledgers = [ model for model in stock_result.models_to_save if isinstance(model, StockTransaction) ] for ledger in ledgers: interface.ledger_processor.rebuild_ledger_state(**ledger.ledger_reference._asdict()) save and SubmissionPost.do_post_save_actions(casedb, [form], case_stock_result) return ReprocessingResult(form, cases, ledgers, None)
def reprocess_form(form, save=True, lock_form=True): if lock_form: # track load if locking; otherise it will be tracked elsewhere form_load_counter("reprocess_form", form.domain)() interface = FormProcessorInterface(form.domain) lock = interface.acquire_lock_for_xform(form.form_id) if lock_form else None with LockManager(form, lock): logger.info('Reprocessing form: %s (%s)', form.form_id, form.domain) # reset form state prior to processing if should_use_sql_backend(form.domain): form.state = XFormInstanceSQL.NORMAL else: form.doc_type = 'XFormInstance' cache = interface.casedb_cache( domain=form.domain, lock=True, deleted_ok=True, xforms=[form], load_src="reprocess_form", ) with cache as casedb: try: case_stock_result = SubmissionPost.process_xforms_for_cases([form], casedb) except (IllegalCaseId, UsesReferrals, MissingProductId, PhoneDateValueError, InvalidCaseIndex, CaseValueError) as e: error_message = '{}: {}'.format(type(e).__name__, six.text_type(e)) form = interface.xformerror_from_xform_instance(form, error_message) return ReprocessingResult(form, [], [], error_message) form.initial_processing_complete = True form.problem = None stock_result = case_stock_result.stock_result assert stock_result.populated cases = case_stock_result.case_models _log_changes(cases, stock_result.models_to_save, stock_result.models_to_delete) ledgers = [] if should_use_sql_backend(form.domain): cases_needing_rebuild = _get_case_ids_needing_rebuild(form, cases) ledgers = stock_result.models_to_save ledgers_updated = {ledger.ledger_reference for ledger in ledgers if ledger.is_saved()} if save: for case in cases: CaseAccessorSQL.save_case(case) LedgerAccessorSQL.save_ledger_values(ledgers) FormAccessorSQL.update_form_problem_and_state(form) FormProcessorSQL.publish_changes_to_kafka(ProcessedForms(form, None), cases, stock_result) # rebuild cases and ledgers that were affected for case in cases: if case.case_id in cases_needing_rebuild: logger.info('Rebuilding case: %s', case.case_id) if save: # only rebuild cases that were updated detail = FormReprocessRebuild(form_id=form.form_id) interface.hard_rebuild_case(case.case_id, detail, lock=False) for ledger in ledgers: if ledger.ledger_reference in ledgers_updated: logger.info('Rebuilding ledger: %s', ledger.ledger_reference) if save: # only rebuild updated ledgers interface.ledger_processor.rebuild_ledger_state(**ledger.ledger_reference._asdict()) else:
case, lock_obj = FormProcessorCouch.get_case_with_lock(case_id, lock=lock, wrap=True) found = bool(case) if not found: case = CommCareCase() case.case_id = case_id case.domain = domain if lock: lock_obj = CommCareCase.get_obj_lock_by_id(case_id) acquire_lock(lock_obj, degrade_gracefully=False) try: assert case.domain == domain, (case.domain, domain) forms = FormProcessorCouch.get_case_forms(case_id) form_load_counter("rebuild_case", domain)(len(forms)) filtered_forms = [f for f in forms if f.is_normal] sorted_forms = sorted(filtered_forms, key=lambda f: f.received_on) actions = _get_actions_from_forms(domain, sorted_forms, case_id) if not found and case.domain is None: case.domain = domain rebuild_case_from_actions(case, actions) # todo: should this move to case.rebuild? if not case.xform_ids: if not found: return None # there were no more forms. 'delete' the case case.doc_type = 'CommCareCase-Deleted'