class TestCheckActionOrder(SimpleTestCase): def test_already_sorted(self): case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 1, 1, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 2, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 3, 0, 0, 0)), ]) self.assertTrue(CouchCaseUpdateStrategy(case).check_action_order())
def get_stock_actions(xform): if is_device_report(xform): return _empty_actions() stock_report_helpers = list(unpack_commtrack(xform)) transaction_helpers = [ transaction_helper for stock_report_helper in stock_report_helpers for transaction_helper in stock_report_helper.transactions ] if not transaction_helpers: return _empty_actions() # list of cases that had stock reports in the form case_ids = list(set(transaction_helper.case_id for transaction_helper in transaction_helpers)) user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] case_action_intents = [] for case_id in case_ids: if is_deprecation(xform): case_action_intents.append(CaseActionIntent( case_id=case_id, form_id=xform.orig_id, is_deprecation=True, action=None )) else: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) case_action_intents.append(CaseActionIntent( case_id=case_id, form_id=xform._id, is_deprecation=False, action=case_action )) return StockFormActions(stock_report_helpers, case_action_intents)
def testActionEquality(self): case_id = _post_util(create=True) _post_util(case_id=case_id, p1='p1', p2='p2') case = CommCareCase.get(case_id) self.assertEqual(2, len(case.actions)) # create + update self.assertTrue(case.actions[0] != case.actions[1]) self.assertTrue(case.actions[1] == case.actions[1]) orig = case.actions[1] copy = CommCareCaseAction.wrap(orig._doc.copy()) self.assertTrue(copy != case.actions[0]) self.assertTrue(copy == orig) copy.server_date = copy.server_date + timedelta(seconds=1) self.assertTrue(copy != orig) copy.server_date = orig.server_date self.assertTrue(copy == orig) copy.updated_unknown_properties['p1'] = 'not-p1' self.assertTrue(copy != orig) copy.updated_unknown_properties['p1'] = 'p1' self.assertTrue(copy == orig) copy.updated_unknown_properties['pnew'] = '' self.assertTrue(copy != orig)
def _rebuild_action(): now = datetime.datetime.utcnow() return CommCareCaseAction( action_type=const.CASE_ACTION_REBUILD, date=now, server_date=now, )
def _make_action(server_date, phone_date=EMPTY_DATE, action_type=const.CASE_ACTION_UPDATE, user_id='someuserid', form_id=None): if phone_date == EMPTY_DATE: phone_date = server_date form_id = form_id or uuid.uuid4().hex return CommCareCaseAction( action_type=action_type, server_date=server_date, date=phone_date, user_id=user_id, xform_id=form_id )
def test_couch_action_equality(self): case_id = _post_util(create=True) _post_util(case_id=case_id, p1='p1', p2='p2') case = CommCareCase.get(case_id) self.assertEqual(3, len(case.actions)) # (1) create & (2) update date opened (3) update properties self.assertTrue(case.actions[0] != case.actions[1]) self.assertTrue(case.actions[1] == case.actions[1]) orig = case.actions[1] copy = CommCareCaseAction.wrap(orig._doc.copy()) self.assertTrue(copy != case.actions[0]) self.assertTrue(copy == orig) copy.server_date = copy.server_date + timedelta(seconds=1) self.assertTrue(copy != orig) copy.server_date = orig.server_date self.assertTrue(copy == orig) copy.updated_unknown_properties['p1'] = 'not-p1' self.assertTrue(copy != orig) copy.updated_unknown_properties['p1'] = 'p1' self.assertTrue(copy == orig) copy.updated_unknown_properties['pnew'] = '' self.assertTrue(copy != orig)
def process_stock(xform, case_db=None): """ process the commtrack xml constructs in an incoming submission """ case_db = case_db or CaseDbCache() assert isinstance(case_db, CaseDbCache) if is_device_report(xform): return [] domain = xform.domain config = CommtrackConfig.for_domain(domain) # these are the raw stock report objects from the xml stock_reports = list(unpack_commtrack(xform, config)) # flattened transaction list spanning all stock reports in the form transactions = [t for r in stock_reports for t in r.transactions] # omitted: normalize_transactions (used for bulk requisitions?) if not transactions: return [] # transactions grouped by case/product id grouped_tx = map_reduce(lambda tx: [((tx.case_id, tx.product_id),)], lambda v: sorted(v, key=lambda tx: tx.timestamp), data=transactions, include_docs=True) case_ids = list(set(k[0] for k in grouped_tx)) # list of cases that had stock reports in the form # there is no need to wrap them by case type relevant_cases = [case_db.get(case_id) for case_id in case_ids] user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case in relevant_cases: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case_db.mark_changed(case) # also purge the sync token cache for the same reason if relevant_cases and xform.get_sync_token(): xform.get_sync_token().invalidate_cached_payloads() # create the django models for report in stock_reports: report.create_models(domain) # TODO make this a signal from corehq.apps.commtrack.signals import send_notifications, raise_events send_notifications(xform, relevant_cases) raise_events(xform, relevant_cases) return relevant_cases
def _send_case_to_es(self, domain=None, owner_id=None, user_id=None, case_type=None, opened_on=None, closed_on=None): actions = [ CommCareCaseAction( action_type=CASE_ACTION_CREATE, date=opened_on, ) ] case = CommCareCase( _id=uuid.uuid4().hex, domain=domain or self.domain, owner_id=owner_id or self.owner_id, user_id=user_id or self.user_id, type=case_type or self.case_type, opened_on=opened_on or datetime.now(), opened_by=user_id or self.user_id, modified_on=datetime.now(), closed_on=closed_on, closed_by=user_id or self.user_id, actions=actions, ) send_to_elasticsearch('cases', case.to_json()) self.es.indices.refresh(CASE_INDEX_INFO.index) return case
def test_update_dependent_case_owner_still_present(self): sync_log = SimplifiedSyncLog( domain="domain", case_ids_on_phone={'c1', 'd1'}, dependent_case_ids_on_phone={'d1'}, index_tree=IndexTree(indices={'c1': { 'd1-id': 'd1' }}), user_id="user", owner_ids_on_phone={'user1'}) dependent_case_state = CaseState(case_id="d1", indices=[]) xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [ CommCareCaseAction(action_type=CASE_ACTION_UPDATE, updated_known_properties={'owner_id': 'user2'}) ] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='d1') # before this test was added, the following call raised a ValueError on legacy logs. sync_log.update_phone_lists(xform, [parent_case]) self.assertIn(dependent_case_state, sync_log.test_only_get_dependent_cases_on_phone())
def allocated_to(self): if self.status == "Closed": close_action = [CommCareCaseAction.wrap(a) for a in self.case['actions'] if a['action_type'] == const.CASE_ACTION_CLOSE][0] CATI_FOLLOW_UP_FORMS = ( "http://openrosa.org/formdesigner/A5B08D8F-139D-46C6-9FDF-B1AD176EAE1F", ) if close_action.xform.xmlns in CATI_FOLLOW_UP_FORMS: return 'CATI' else: return 'Field' else: follow_up_type = self.case.get('follow_up_type', '') house_number = self.case.get('phone_house_number', '') husband_number = self.case.get('phone_husband_number', '') mother_number = self.case.get('phone_mother_number', '') asha_number = self.case.get('phone_asha_number', '') if follow_up_type != 'field_follow_up' and (house_number or husband_number or mother_number or asha_number): return 'CATI' else: return 'Field'
def test_couch_action_equality(self): case_id = _post_util(create=True) _post_util(case_id=case_id, p1='p1', p2='p2') case = CommCareCase.get(case_id) self.assertEqual(3, len(case.actions)) # (1) create & (2) update date opened (3) update properties self.assertTrue(case.actions[0] != case.actions[1]) self.assertTrue(case.actions[1] == case.actions[1]) orig = case.actions[2] copy = CommCareCaseAction.wrap(deepcopy(orig._doc)) self.assertTrue(copy != case.actions[0]) self.assertTrue(copy == orig) copy.server_date = copy.server_date + timedelta(seconds=1) self.assertTrue(copy != orig) copy.server_date = orig.server_date self.assertTrue(copy == orig) copy.updated_unknown_properties['p1'] = 'not-p1' self.assertTrue(copy != orig) copy.updated_unknown_properties['p1'] = 'p1' self.assertTrue(copy == orig) copy.updated_unknown_properties['pnew'] = '' self.assertTrue(copy != orig)
def allocated_to(self): if self.status == "Closed": close_action = [ CommCareCaseAction.wrap(a) for a in self.case['actions'] if a['action_type'] == const.CASE_ACTION_CLOSE ][0] CATI_FOLLOW_UP_FORMS = ( "http://openrosa.org/formdesigner/A5B08D8F-139D-46C6-9FDF-B1AD176EAE1F", ) if close_action.xform.xmlns in CATI_FOLLOW_UP_FORMS: return 'CATI' else: return 'Field' else: follow_up_type = self.case.get('follow_up_type', '') house_number = self.case.get('phone_house_number', '') husband_number = self.case.get('phone_husband_number', '') mother_number = self.case.get('phone_mother_number', '') asha_number = self.case.get('phone_asha_number', '') if follow_up_type != 'field_follow_up' and (house_number or husband_number or mother_number or asha_number): return 'CATI' else: return 'Field'
def test_update_dependent_case_owner_still_present(self): dependent_case_state = CaseState(case_id="d1", indices=[]) sync_log = SyncLog(domain="domain", user_id="user", cases_on_phone=[ CaseState(case_id="c1", indices=[ CommCareCaseIndex( identifier="d1-id", referenced_id="d1") ]) ], dependent_cases_on_phone=[dependent_case_state], owner_ids_on_phone=['user1']) xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [ CommCareCaseAction(action_type=CASE_ACTION_UPDATE, updated_known_properties={'owner_id': 'user2'}) ] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='d1') # before this test was added, the following call raised a ValueError on legacy logs. for log in [ sync_log, SimplifiedSyncLog.from_other_format(sync_log) ]: log.update_phone_lists(xform, [parent_case]) self.assertIn(dependent_case_state, log.test_only_get_dependent_cases_on_phone())
def test_update_dependent_case(self): sync_log = SyncLog( cases_on_phone=[ CaseState( case_id='bran', indices=[ CommCareCaseIndex(identifier='legs', referenced_id='hodor') ], ), ], dependent_cases_on_phone=[CaseState(case_id='hodor')], user_id="someuser") xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [CommCareCaseAction(action_type=CASE_ACTION_UPDATE, )] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='hodor') # before this test was added, the following call raised a SyncLogAssertionError on legacy logs. # this test just ensures it doesn't still do that. for log in [ sync_log, SimplifiedSyncLog.from_other_format(sync_log) ]: log.update_phone_lists(xform, [parent_case])
def get_stock_actions(xform): if is_device_report(xform): return [], [] stock_report_helpers = list(unpack_commtrack(xform)) transaction_helpers = [ transaction_helper for stock_report_helper in stock_report_helpers for transaction_helper in stock_report_helper.transactions ] if not transaction_helpers: return [], [] # list of cases that had stock reports in the form case_ids = list(set(transaction_helper.case_id for transaction_helper in transaction_helpers)) user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] case_actions = [] for case_id in case_ids: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) case_actions.append((case_id, case_action)) return stock_report_helpers, case_actions
def get_couch_action(self): assert self.action_type == CASE_ACTION_COMMTRACK return CommCareCaseAction.from_parsed_action( date=self.form.received_on, user_id=self.form.metadata.userID, xformdoc=self.form, action=AbstractAction(self.action_type), )
def process_stock(xform, case_db=None): """ process the commtrack xml constructs in an incoming submission """ case_db = case_db or CaseDbCache() assert isinstance(case_db, CaseDbCache) if is_device_report(xform): return StockProcessingResult(xform) stock_report_helpers = list(unpack_commtrack(xform)) transaction_helpers = [ transaction_helper for stock_report_helper in stock_report_helpers for transaction_helper in stock_report_helper.transactions ] # omitted: normalize_transactions (used for bulk requisitions?) if not transaction_helpers: return StockProcessingResult(xform) # validate product ids if any(transaction_helper.product_id in ('', None) for transaction_helper in transaction_helpers): raise MissingProductId( _('Product IDs must be set for all ledger updates!')) # list of cases that had stock reports in the form # there is no need to wrap them by case type case_ids = list(set(transaction_helper.case_id for transaction_helper in transaction_helpers)) relevant_cases = [case_db.get(case_id) for case_id in case_ids] user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case_id, case in zip(case_ids, relevant_cases): if case is None: raise IllegalCaseId( _('Ledger transaction references invalid Case ID "{}"') .format(case_id)) case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case_db.mark_changed(case) return StockProcessingResult( xform=xform, relevant_cases=relevant_cases, stock_report_helpers=stock_report_helpers, )
def process_stock(xform): """ process the commtrack xml constructs in an incoming submission """ if is_device_report(xform): return domain = xform.domain config = CommtrackConfig.for_domain(domain) # these are the raw stock report objects from the xml stock_reports = list(unpack_commtrack(xform, config)) # flattened transaction list spanning all stock reports in the form transactions = [t for r in stock_reports for t in r.transactions] # omitted: normalize_transactions (used for bulk requisitions?) if not transactions: return # transactions grouped by case/product id grouped_tx = map_reduce(lambda tx: [((tx.case_id, tx.product_id),)], lambda v: sorted(v, key=lambda tx: tx.timestamp), data=transactions, include_docs=True) # list of cases that had stock reports in the form, properly wrapped by case type try: relevant_cases = [wrap_commtrack_case(result['doc']) for result in CommCareCase.get_db().view('_all_docs', keys=list(set(k[0] for k in grouped_tx)), include_docs=True)] except KeyError: raise Exception("Cannot find case matching supplied entity id") user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case in relevant_cases: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case.save() # create the django models for report in stock_reports: report.create_models() # TODO make this a signal from corehq.apps.commtrack.signals import send_notifications, raise_events send_notifications(xform, relevant_cases) raise_events(xform, relevant_cases)
def creating_user(self): creator_id = None for action in self.case['actions']: if action['action_type'] == 'create': action_doc = CommCareCaseAction.wrap(action) creator_id = action_doc.get_user_id() break if not creator_id: return None return self._user_meta(creator_id)
def creating_user(self): creator_id = None for action in self.case['actions']: if action['action_type'] == 'create': action_doc = CommCareCaseAction.wrap(action) creator_id = action_doc.get_user_id() break if not creator_id: return _("No data") return self._get_username(creator_id)
def _make_action(server_date, phone_date=EMPTY_DATE, action_type=const.CASE_ACTION_UPDATE, user_id='someuserid'): if phone_date == EMPTY_DATE: phone_date = server_date return CommCareCaseAction(action_type=action_type, server_date=server_date, date=phone_date, user_id=user_id)
def get_case_actions(self, xformdoc): """ Gets case actions from this object. These are the actual objects that get stored in the CommCareCase model (as opposed to the parser's representation of those) """ return [ CommCareCaseAction.from_parsed_action( self.guess_modified_on(), self.user_id, xformdoc, action ) for action in self.actions ]
def get_case_actions(self, xformdoc): """ Gets case actions from this object. These are the actual objects that get stored in the CommCareCase model (as opposed to the parser's representation of those) """ return [ CommCareCaseAction.from_parsed_action(self.guess_modified_on(), self.user_id, xformdoc, action) for action in self.actions ]
def process_stock(xform, case_db=None): """ process the commtrack xml constructs in an incoming submission """ case_db = case_db or CaseDbCache() assert isinstance(case_db, CaseDbCache) if is_device_report(xform): return StockProcessingResult(xform) # these are the raw stock report objects from the xml stock_reports = list(unpack_commtrack(xform)) # flattened transaction list spanning all stock reports in the form transactions = [t for r in stock_reports for t in r.transactions] # omitted: normalize_transactions (used for bulk requisitions?) if not transactions: return StockProcessingResult(xform) # validate product ids is_empty = lambda product_id: product_id is None or product_id == '' if any([is_empty(tx.product_id) for tx in transactions]): raise MissingProductId( _('Product IDs must be set for all ledger updates!')) # transactions grouped by case/product id grouped_tx = map_reduce(lambda tx: [((tx.case_id, tx.product_id),)], lambda v: sorted(v, key=lambda tx: tx.timestamp), data=transactions, include_docs=True) case_ids = list(set(k[0] for k in grouped_tx)) # list of cases that had stock reports in the form # there is no need to wrap them by case type relevant_cases = [case_db.get(case_id) for case_id in case_ids] user_id = xform.form['meta']['userID'] submit_time = xform['received_on'] # touch every case for proper ota restore logic syncing to be preserved for case in relevant_cases: case_action = CommCareCaseAction.from_parsed_action( submit_time, user_id, xform, AbstractAction(CASE_ACTION_COMMTRACK) ) # hack: clear the sync log id so this modification always counts # since consumption data could change server-side case_action.sync_log_id = '' case.actions.append(case_action) case_db.mark_changed(case) return StockProcessingResult( xform=xform, relevant_cases=relevant_cases, stock_report_helpers=stock_reports, )
def _creating_user(self): try: creator_id = self.raw_data['opened_by'] except KeyError: creator_id = None if 'actions' in self.raw_data: for action in self.raw_data['actions']: if action['action_type'] == 'create': action_doc = CommCareCaseAction.wrap(action) creator_id = action_doc.get_user_id() break if not creator_id: return None return self._user_meta(creator_id)
def creating_user(self): try: creator_id = self.case['opened_by'] except KeyError: creator_id = None if 'actions' in self.case: for action in self.case['actions']: if action['action_type'] == 'create': action_doc = CommCareCaseAction.wrap(action) creator_id = action_doc.get_user_id() break if not creator_id: return None return self._user_meta(creator_id)
def prepare_case_json(planning_db): case_ids = planning_db.get_all_case_ids(valid_only=False) for case_json in iter_docs(CommCareCase.get_db(), case_ids): case = CommCareCase.wrap(case_json) if case.doc_type != "CommCareCase": assert case.doc_type == "CommCareCase-Deleted" continue # to normalize for any new fields added case_json = deepcopy(case.to_json()) actions = [CommCareCaseAction.wrap(action) for action in planning_db.get_actions_by_case(case.case_id)] rebuild_case_from_actions(case, actions) planning_db.update_case_json(case.case_id, case.to_json()) planning_db.add_diffs("case", case.case_id, json_diff(case_json, case.to_json())) return planning_db
def test_update_dependent_case(self): sync_log = SimplifiedSyncLog( case_ids_on_phone={'bran', 'hodor'}, dependent_case_ids_on_phone={'hodor'}, index_tree=IndexTree(indices={'bran': { 'legs': 'hodor' }}), user_id="someuser") xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [CommCareCaseAction(action_type=CASE_ACTION_UPDATE, )] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='hodor') # before this test was added, the following call raised a SyncLogAssertionError on legacy logs. # this test just ensures it doesn't still do that. sync_log.update_phone_lists(xform, [parent_case])
def _copy_unprocessed_case(self, change): couch_case = CommCareCase.wrap(change.get_document()) log.debug('Processing doc: {}({})'.format(couch_case['doc_type'], change.id)) try: first_action = couch_case.actions[0] except IndexError: first_action = CommCareCaseAction() sql_case = CommCareCaseSQL( case_id=couch_case.case_id, domain=self.domain, type=couch_case.type or '', name=couch_case.name, owner_id=couch_case.owner_id or couch_case.user_id or '', opened_on=couch_case.opened_on or first_action.date, opened_by=couch_case.opened_by or first_action.user_id, modified_on=couch_case.modified_on, modified_by=couch_case.modified_by or couch_case.user_id or '', server_modified_on=couch_case.server_modified_on, closed=couch_case.closed, closed_on=couch_case.closed_on, closed_by=couch_case.closed_by, deleted=True, deletion_id=couch_case.deletion_id, deleted_on=couch_case.deletion_date, external_id=couch_case.external_id, case_json=couch_case.dynamic_case_properties() ) _migrate_case_actions(couch_case, sql_case) _migrate_case_indices(couch_case, sql_case) _migrate_case_attachments(couch_case, sql_case) try: CaseAccessorSQL.save_case(sql_case) except IntegrityError: # case re-created by form processing so just mark the case as deleted CaseAccessorSQL.soft_delete_cases( self.domain, [sql_case.case_id], sql_case.deleted_on, sql_case.deletion_id ) self.processed_docs += 1 self._log_unprocessed_cases_processed_count(throttled=True)
def _copy_unprocessed_cases(self): doc_types = ['CommCareCase-Deleted'] changes = _get_case_iterator(self.domain, doc_types=doc_types).iter_all_changes() for change in self._with_progress(doc_types, changes): couch_case = CommCareCase.wrap(change.get_document()) self.log_debug('Processing doc: {}({})'.format( couch_case['doc_type'], change.id)) try: first_action = couch_case.actions[0] except IndexError: first_action = CommCareCaseAction() sql_case = CommCareCaseSQL( case_id=couch_case.case_id, domain=self.domain, type=couch_case.type or '', name=couch_case.name, owner_id=couch_case.owner_id or couch_case.user_id or '', opened_on=couch_case.opened_on or first_action.date, opened_by=couch_case.opened_by or first_action.user_id, modified_on=couch_case.modified_on, modified_by=couch_case.modified_by or couch_case.user_id or '', server_modified_on=couch_case.server_modified_on, closed=couch_case.closed, closed_on=couch_case.closed_on, closed_by=couch_case.closed_by, deleted=True, deletion_id=couch_case.deletion_id, deleted_on=couch_case.deletion_date, external_id=couch_case.external_id, case_json=couch_case.dynamic_case_properties()) _migrate_case_actions(couch_case, sql_case) _migrate_case_indices(couch_case, sql_case) _migrate_case_attachments(couch_case, sql_case) try: CaseAccessorSQL.save_case(sql_case) except IntegrityError as e: self.log_error("Unable to migrate case:\n{}\n{}".format( couch_case.case_id, e))
def _copy_unprocessed_case(self, doc): couch_case = CommCareCase.wrap(doc) log.debug('Processing doc: %(doc_type)s(%(_id)s)', doc) try: first_action = couch_case.actions[0] except IndexError: first_action = CommCareCaseAction() opened_on = couch_case.opened_on or first_action.date sql_case = CommCareCaseSQL( case_id=couch_case.case_id, domain=self.domain, type=couch_case.type or '', name=couch_case.name, owner_id=couch_case.owner_id or couch_case.user_id or '', opened_on=opened_on, opened_by=couch_case.opened_by or first_action.user_id, modified_on=couch_case.modified_on or opened_on, modified_by=couch_case.modified_by or couch_case.user_id or '', server_modified_on=couch_case.server_modified_on, closed=couch_case.closed, closed_on=couch_case.closed_on, closed_by=couch_case.closed_by, deleted=True, deletion_id=couch_case.deletion_id, deleted_on=couch_case.deletion_date, external_id=couch_case.external_id, case_json=couch_case.dynamic_case_properties()) _migrate_case_actions(couch_case, sql_case) _migrate_case_indices(couch_case, sql_case) _migrate_case_attachments(couch_case, sql_case) try: CaseAccessorSQL.save_case(sql_case) except IntegrityError: # case re-created by form processing so just mark the case as deleted CaseAccessorSQL.soft_delete_cases(self.domain, [sql_case.case_id], sql_case.deleted_on, sql_case.deletion_id) finally: self.case_diff_queue.enqueue(couch_case.case_id)
rebuild_case_from_forms(REBUILD_TEST_DOMAIN, child_case_id, RebuildWithReason(reason='test')) class TestCheckActionOrder(SimpleTestCase): def test_already_sorted(self): case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 1, 1, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 2, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 3, 0, 0, 0)), ]) self.assertTrue(CouchCaseUpdateStrategy(case).check_action_order()) def test_out_of_order(self): case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 1, 1, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 3, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 2, 0, 0, 0)), ]) self.assertFalse(CouchCaseUpdateStrategy(case).check_action_order()) def test_sorted_with_none(self): case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 1, 1, 0, 0, 0)), CommCareCaseAction(server_date=None), CommCareCaseAction(server_date=datetime(2001, 1, 2, 0, 0, 0)), CommCareCaseAction(server_date=datetime(2001, 1, 3, 0, 0, 0)), ]) self.assertTrue(CouchCaseUpdateStrategy(case).check_action_order()) def test_out_of_order_with_none(self):
def test_couch_action_not_equals(self): orig = CommCareCaseAction() copy = CommCareCaseAction.wrap(deepcopy(orig._doc)) self.assertTrue(orig == copy) self.assertFalse(orig != copy)
def _action(self, datetime_): return CommCareCaseAction(server_date=datetime_, date=datetime_, action_type='update')
class TestCheckActionOrder(TestCase): def test(self): case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 01, 01, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 02, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 03, 00, 00, 00)), ]) self.assertTrue(case.check_action_order()) case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 01, 01, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 03, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 02, 00, 00, 00)), ]) self.assertFalse(case.check_action_order()) case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 01, 01, 00, 00, 00)), CommCareCaseAction(server_date=None), CommCareCaseAction(server_date=datetime(2001, 01, 02, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 03, 00, 00, 00)), ]) self.assertTrue(case.check_action_order()) case = CommCareCase(actions=[ CommCareCaseAction(server_date=datetime(2001, 01, 01, 00, 00, 00)), CommCareCaseAction(server_date=datetime(2001, 01, 03, 00, 00, 00)), CommCareCaseAction(server_date=None), CommCareCaseAction(server_date=datetime(2001, 01, 02, 00, 00, 00)), ]) self.assertFalse(case.check_action_order())
]) return prepare_case_json(planning_db) def prepare_case_json(planning_db): case_ids = planning_db.get_all_case_ids(valid_only=False) for case_json in iter_docs(CommCareCase.get_db(), case_ids): case = CommCareCase.wrap(case_json) if case.doc_type != 'CommCareCase': assert case.doc_type == 'CommCareCase-Deleted' continue # to normalize for any new fields added case_json = deepcopy(case.to_json()) actions = [ CommCareCaseAction.wrap(action) for action in planning_db.get_actions_by_case(case.case_id) ] rebuild_case_from_actions(case, actions) planning_db.update_case_json(case.case_id, case.to_json()) planning_db.add_diffs('case', case.case_id, json_diff(case_json, case.to_json())) return planning_db def is_datetime_string(string): if not isinstance(string, str): return False try: iso_string_to_datetime(string, strict=True)