Ejemplo n.º 1
0
    def test_couch_reconcile_actions(self):
        now = datetime.utcnow()
        # make sure we timestamp everything so they have the right order
        case_id = _post_util(create=True, form_extras={'received_on': now})
        _post_util(case_id=case_id, p1='p1-1', p2='p2-1', form_extras={'received_on': now + timedelta(seconds=1)})
        _post_util(case_id=case_id, p2='p2-2', p3='p3-2', form_extras={'received_on': now + timedelta(seconds=2)})
        case = CommCareCase.get(case_id)
        update_strategy = CouchCaseUpdateStrategy(case)

        original_actions = [deepcopy(a) for a in case.actions]
        original_form_ids = [id for id in case.xform_ids]
        self.assertEqual(4, len(original_actions))
        self.assertEqual(3, len(original_form_ids))
        self._assertListEqual(original_actions, case.actions)

        # test reordering
        case.actions = [case.actions[3], case.actions[2], case.actions[1], case.actions[0]]
        self._assertListNotEqual(original_actions, case.actions)
        update_strategy.reconcile_actions()
        self._assertListEqual(original_actions, case.actions)

        # test duplication
        case.actions = case.actions * 3
        self.assertEqual(12, len(case.actions))
        self._assertListNotEqual(original_actions, case.actions)
        update_strategy.reconcile_actions()
        self._assertListEqual(original_actions, case.actions)

        # test duplication, even when dates are off
        case.actions = original_actions + [deepcopy(case.actions[2])]
        case.actions[-1].server_date = case.actions[-1].server_date + timedelta(seconds=1)
        self._assertListNotEqual(original_actions, case.actions)
        update_strategy.reconcile_actions()
        self._assertListEqual(original_actions, case.actions)

        # test duplication with different properties is actually
        # treated differently
        case.actions = original_actions + [deepcopy(case.actions[2])]
        case.actions[-1].updated_unknown_properties['new'] = 'mismatch'
        self.assertEqual(5, len(case.actions))
        self._assertListNotEqual(original_actions, case.actions)
        update_strategy.reconcile_actions()
        self._assertListNotEqual(original_actions, case.actions)

        # test clean slate rebuild
        case = rebuild_case_from_forms(REBUILD_TEST_DOMAIN, case_id, RebuildWithReason(reason='test'))
        self._assertListEqual(original_actions, primary_actions(case))
        self._assertListEqual(original_form_ids, case.xform_ids)
Ejemplo n.º 2
0
    def reconcile_transactions(self):
        transactions = self.case.transactions
        sorted_transactions = sorted(transactions,
                                     key=_transaction_sort_key_function(
                                         self.case))
        if sorted_transactions:
            if not sorted_transactions[0].is_case_create:
                error = "Case {0} first transaction not create transaction: {1}"
                raise ReconciliationError(
                    error.format(self.case.case_id, sorted_transactions[0]))

        self._fetch_case_transaction_forms(sorted_transactions)
        rebuild_detail = RebuildWithReason(reason="client_date_reconciliation")
        rebuild_transaction = CaseTransaction.rebuild_transaction(
            self.case, rebuild_detail)
        self.rebuild_from_transactions(sorted_transactions,
                                       rebuild_transaction)
Ejemplo n.º 3
0
    def test_couch_rebuild_deleted_case(self):
        # Note: Can't run this on SQL because if a case gets hard deleted then
        # there is no way to find out which forms created / updated it without
        # going through ALL the forms in the domain. ie. there is no SQL
        # equivalent to the "form_case_index/form_case_index" couch view

        case_id = _post_util(create=True)
        _post_util(case_id=case_id, p1='p1', p2='p2')

        # delete initial case
        delete_all_cases()

        with self.assertRaises(CaseNotFound):
            CaseAccessors(REBUILD_TEST_DOMAIN).get_case(case_id)

        case = rebuild_case_from_forms(REBUILD_TEST_DOMAIN, case_id, RebuildWithReason(reason='test'))
        self.assertEqual(case.p1, 'p1')
        self.assertEqual(case.p2, 'p2')
        self.assertEqual(3, len(primary_actions(case)))  # create + update
Ejemplo n.º 4
0
    def handle(self, *args, **options):
        if len(args) == 2:
            domain = args[0]
            reason = args[1]
        else:
            raise CommandError('Usage: %s\n%s' % (self.args, self.help))

        if should_use_sql_backend(domain):
            raise CommandError(
                'This command only works for couch-based domains.')

        ids = get_case_ids_in_domain(domain)
        for count, case_id in enumerate(ids):
            try:
                rebuild_case_from_forms(domain, case_id,
                                        RebuildWithReason(reason=reason))
                if count % 100 == 0:
                    print 'rebuilt %s/%s cases' % (count, len(ids))
            except Exception, e:
                logging.exception("couldn't rebuild case {id}. {msg}".format(
                    id=case_id, msg=str(e)))
Ejemplo n.º 5
0
def unsort_transactions(sql_case, commit):
    rebuilds = [t for t in sql_case.transactions if is_rebuild(t)]
    others = [t for t in sql_case.transactions if not is_rebuild(t)]
    assert len(rebuilds) == 1, rebuilds
    changes = []
    for trans in sorted(others, key=lambda t: t.server_date):
        if not trans.form_id:
            continue
        received_on = FormAccessorSQL.get_form(trans.form_id).received_on
        if received_on != trans.server_date:
            changes.append((trans, received_on))
            if commit:
                trans.server_date = received_on
                trans.save()
    case_id = sql_case.case_id
    if changes:
        chg = "\n".join(f"  {t.form_id}: {t.server_date} -> {received_on}"
                        for t, received_on in changes)
        log.info("unsort transactions for case %s:\n%s", case_id, chg)
        if commit:
            detail = RebuildWithReason(reason=UNSORT_REBUILD_REASON)
            rebuild_case(sql_case, detail)
    else:
        log.info("no changes for case %s", case_id)
Ejemplo n.º 6
0
def rebuild_cases(cases_to_rebuild_by_domain, logger):
    detail = RebuildWithReason(reason='undo UUID clash')
    for domain, case_ids in six.iteritems(cases_to_rebuild_by_domain):
        for case_id in case_ids:
            FormProcessorSQL.hard_rebuild_case(domain, case_id, detail)
            logger.log('Case %s rebuilt' % case_id)
Ejemplo n.º 7
0
    def test_ignores_before_rebuild_transaction(self):
        with freeze_time("2018-10-10"):
            case = self._create_case()

        with freeze_time("2018-10-11"):
            new_old_xform = self._create_form()
        with freeze_time("2018-10-08"):
            new_old_trans = self._create_case_transaction(case, new_old_xform)
        with freeze_time("2018-10-11"):
            self._save(new_old_xform, case, new_old_trans)

        self.assertFalse(case.check_transaction_order())

        with freeze_time("2018-10-13"):
            new_rebuild_xform = self._create_form()
            rebuild_detail = RebuildWithReason(reason="shadow's golden coin")
            rebuild_transaction = CaseTransaction.rebuild_transaction(
                case, rebuild_detail)
            self._save(new_rebuild_xform, case, rebuild_transaction)

        case = CaseAccessorSQL.get_case(case.case_id)
        update_strategy = SqlCaseUpdateStrategy(case)
        self.assertFalse(update_strategy.reconcile_transactions_if_necessary())

    def test_first_transaction_not_create(self):
        with freeze_time("2018-10-10"):
            case = self._create_case()

        with freeze_time("2018-10-08"):
            new_old_xform = self._create_form()
            new_old_trans = self._create_case_transaction(case, new_old_xform)
Ejemplo n.º 8
0
 def test_rebuild_empty(self):
     self.assertEqual(
         None,
         rebuild_case_from_forms('anydomain', 'notarealid', RebuildWithReason(reason='test'))
     )
    def test_blank_change(self):
        self.factory.create_or_update_case(
            CaseStructure(self.case.case_id, attrs={
                "update": {
                    'sword': ''
                },
            }), )
        case = CaseAccessors(self.domain).get_case(self.case.case_id)
        changes, _ = get_paged_changes_to_case_property(case, 'sword')
        self.assertEqual(len(changes), 2)
        self.assertEqual(changes[0].new_value, '')
        self.assertEqual(changes[1].new_value, 'Narsil')

    @run_with_all_backends
    def test_case_rebuild(self):
        # Cases with rebuild actions were failing because rebuild actions have no form
        # https://manage.dimagi.com/default.asp?276216#1494409
        self.factory.create_or_update_case(
            CaseStructure(self.case.case_id, attrs={
                "update": {
                    'sword': ''
                },
            }), )
        rebuild_case_from_forms(self.domain, self.case.case_id,
                                RebuildWithReason())
        case = CaseAccessors(self.domain).get_case(self.case.case_id)
        changes, _ = get_paged_changes_to_case_property(case, 'sword')
        self.assertEqual(len(changes), 2)
        self.assertEqual(changes[0].new_value, '')
        self.assertEqual(changes[1].new_value, 'Narsil')
Ejemplo n.º 10
0
def rebuild_case(sql_case, detail=None):
    if detail is None:
        detail = RebuildWithReason(reason=COUCH_SQL_REBUILD_REASON)
    return FormProcessorSQL.hard_rebuild_case(sql_case.domain, sql_case.case_id, detail)
Ejemplo n.º 11
0
def rebuild_cases(cases_to_rebuild_by_domain):
    detail = RebuildWithReason(reason='undo UUID clash')
    for domain, case_ids in cases_to_rebuild_by_domain.iteritems():
        for case_id in case_ids:
            FormProcessorSQL.hard_rebuild_case(domain, case_id, detail)