예제 #1
0
    def rows(self):
        startdate = json_format_datetime(self.datespan.startdate_utc)
        enddate = json_format_datetime(self.datespan.enddate_utc)
        data = FRISMSLog.view("sms/by_domain",
                              startkey=[self.domain, "SMSLog", startdate],
                              endkey=[self.domain, "SMSLog", enddate],
                              include_docs=True,
                              reduce=False).all()
        result = []
        direction_map = {
            INCOMING: _("Incoming"),
            OUTGOING: _("Outgoing"),
        }
        message_bank_messages = get_message_bank(self.domain,
                                                 for_comparing=True)

        case_cache = CaseDbCache(domain=self.domain,
                                 strip_history=False,
                                 deleted_ok=True)
        user_cache = UserCache()

        show_only_survey_traffic = self.show_only_survey_traffic()

        for message in data:
            if message.direction == OUTGOING and not message.processed:
                continue
            if show_only_survey_traffic and message.xforms_session_couch_id is None:
                continue
            # Add metadata from the message bank if it has not been added already
            if (message.direction == OUTGOING) and (
                    not message.fri_message_bank_lookup_completed):
                add_metadata(message, message_bank_messages)

            if message.couch_recipient_doc_type == "CommCareCase":
                recipient = case_cache.get(message.couch_recipient)
            else:
                recipient = user_cache.get(message.couch_recipient)

            if message.chat_user_id:
                sender = user_cache.get(message.chat_user_id)
            else:
                sender = None

            study_arm = None
            if message.couch_recipient_doc_type == "CommCareCase":
                study_arm = case_cache.get(
                    message.couch_recipient).get_case_property("study_arm")

            timestamp = ServerTime(message.date).user_time(
                self.timezone).done()
            result.append([
                self._fmt(self._participant_id(recipient)),
                self._fmt(study_arm or "-"),
                self._fmt(self._originator(message, recipient, sender)),
                self._fmt_timestamp(timestamp),
                self._fmt(message.text),
                self._fmt(message.fri_id or "-"),
                self._fmt(direction_map.get(message.direction, "-")),
            ])
        return result
예제 #2
0
 def test_wrap_lock_dependency(self):
     # valid combinations
     CaseDbCache(domain='some-domain', lock=False, wrap=True)
     CaseDbCache(domain='some-domain', lock=False, wrap=False)
     CaseDbCache(domain='some-domain', lock=True, wrap=True)
     with self.assertRaises(ValueError):
         # invalid
         CaseDbCache(domain='some-domain', lock=True, wrap=False)
예제 #3
0
    def testGetPopulatesCache(self):
        case_ids = _make_some_cases(3)
        cache = CaseDbCache()
        for id in case_ids:
            self.assertFalse(cache.in_cache(id))

        for i, id in enumerate(case_ids):
            case = cache.get(id)
            self.assertEqual(str(i), case.my_index)
예제 #4
0
def get_related_cases(initial_cases, domain, strip_history=False, search_up=True):
    """
    Gets the flat list of related cases based on a starting list.
    Walks all the referenced indexes recursively.
    If search_up is True, all cases and their parent cases are returned.
    If search_up is False, all cases and their child cases are returned.
    """
    if not initial_cases:
        return {}

    # infer whether to wrap or not based on whether the initial list is wrapped or not
    # initial_cases may be a list or a set
    wrap = isinstance(next(iter(initial_cases)), CommCareCase)

    # todo: should assert that domain exists here but this breaks tests
    case_db = CaseDbCache(domain=domain,
                          strip_history=strip_history,
                          deleted_ok=True,
                          wrap=wrap,
                          initial=initial_cases)

    def indices(case):
        return case['indices'] if search_up else reverse_indices(CommCareCase.get_db(), case, wrap=False)

    relevant_cases = {}
    relevant_deleted_case_ids = []

    cases_to_process = list(case for case in initial_cases)
    directly_referenced_indices = itertools.chain(
        *[[index['referenced_id'] for index in indices(case)]
          for case in initial_cases]
    )
    case_db.populate(directly_referenced_indices)

    def process_cases(cases):
        new_relations = set()
        for case in cases:
            if case and case['_id'] not in relevant_cases:
                relevant_cases[case['_id']] = case
                if case['doc_type'] == 'CommCareCase-Deleted':
                    relevant_deleted_case_ids.append(case['_id'])
                new_relations.update(index['referenced_id'] for index in indices(case))

        if new_relations:
            case_db.populate(new_relations)
            return [case_db.get(related_case) for related_case in new_relations]

    while cases_to_process:
        cases_to_process = process_cases(cases_to_process)

    if relevant_deleted_case_ids:
        logging.info('deleted cases included in footprint (restore): %s' % (
            ', '.join(relevant_deleted_case_ids)
        ))

    return relevant_cases
예제 #5
0
    def rows(self):
        startdate = json_format_datetime(self.datespan.startdate_utc)
        enddate = json_format_datetime(self.datespan.enddate_utc)
        data = FRISMSLog.view("sms/by_domain",
                              startkey=[self.domain, "SMSLog", startdate],
                              endkey=[self.domain, "SMSLog", enddate],
                              include_docs=True,
                              reduce=False).all()
        result = []
        direction_map = {
            INCOMING: _("Incoming"),
            OUTGOING: _("Outgoing"),
        }
        message_bank_messages = get_message_bank(self.domain, for_comparing=True)

        case_cache = CaseDbCache(domain=self.domain, strip_history=False, deleted_ok=True)
        user_cache = UserCache()

        show_only_survey_traffic = self.show_only_survey_traffic()

        for message in data:
            if message.direction == OUTGOING and not message.processed:
                continue
            if show_only_survey_traffic and message.xforms_session_couch_id is None:
                continue
            # Add metadata from the message bank if it has not been added already
            if (message.direction == OUTGOING) and (not message.fri_message_bank_lookup_completed):
                add_metadata(message, message_bank_messages)

            if message.couch_recipient_doc_type == "CommCareCase":
                recipient = case_cache.get(message.couch_recipient)
            else:
                recipient = user_cache.get(message.couch_recipient)

            if message.chat_user_id:
                sender = user_cache.get(message.chat_user_id)
            else:
                sender = None

            study_arm = None
            if message.couch_recipient_doc_type == "CommCareCase":
                study_arm = case_cache.get(message.couch_recipient).get_case_property("study_arm")

            timestamp = ServerTime(message.date).user_time(self.timezone).done()
            result.append([
                self._fmt(self._participant_id(recipient)),
                self._fmt(study_arm or "-"),
                self._fmt(self._originator(message, recipient, sender)),
                self._fmt_timestamp(timestamp),
                self._fmt(message.text),
                self._fmt(message.fri_id or "-"),
                self._fmt(direction_map.get(message.direction,"-")),
            ])
        return result
예제 #6
0
    def testGetPopulatesCache(self):
        case_ids = _make_some_cases(3)
        cache = CaseDbCache()
        for id in case_ids:
            self.assertFalse(cache.in_cache(id))

        for i, id in enumerate(case_ids):
            case = cache.get(id)
            self.assertEqual(str(i), case.my_index)

        for id in case_ids:
            self.assertTrue(cache.in_cache(id))
예제 #7
0
    def testDocTypeCheck(self):
        id = uuid.uuid4().hex
        CommCareCase.get_db().save_doc({
            "_id": id,
            "doc_type": "AintNoCasesHere"
        })
        cache = CaseDbCache()
        try:
            cache.get(id)
            self.fail('doc type security check failed to raise exception')
        except IllegalCaseId:
            pass

        doc_back = CommCareCase.get_db().get(id)
        self.assertEqual("AintNoCasesHere", doc_back['doc_type'])
예제 #8
0
    def testDocTypeCheck(self):
        id = uuid.uuid4().hex
        CommCareCase.get_db().save_doc({
            "_id": id,
            "doc_type": "AintNoCasesHere"
        })
        cache = CaseDbCache()
        try:
            cache.get(id)
            self.fail('doc type security check failed to raise exception')
        except IllegalCaseId:
            pass

        doc_back = CommCareCase.get_db().get(id)
        self.assertEqual("AintNoCasesHere", doc_back['doc_type'])
예제 #9
0
def get_related_cases(initial_cases, domain, strip_history=False, search_up=True):
    """
    Gets the flat list of related cases based on a starting list.
    Walks all the referenced indexes recursively.
    If search_up is True, all cases and their parent cases are returned.
    If search_up is False, all cases and their child cases are returned.
    """
    if not initial_cases:
        return {}

    # infer whether to wrap or not based on whether the initial list is wrapped or not
    # initial_cases may be a list or a set
    wrap = isinstance(next(iter(initial_cases)), CommCareCase)

    # todo: should assert that domain exists here but this breaks tests
    case_db = CaseDbCache(domain=domain,
                          strip_history=strip_history,
                          deleted_ok=True,
                          wrap=wrap,
                          initial=initial_cases)

    def indices(case):
        return case['indices'] if search_up else get_reverse_indices_json(case)

    relevant_cases = {}
    relevant_deleted_case_ids = []

    cases_to_process = list(case for case in initial_cases)
예제 #10
0
def get_related_cases(initial_case_list,
                      domain,
                      strip_history=False,
                      search_up=True):
    """
    Gets the flat list of related cases based on a starting list.
    Walks all the referenced indexes recursively.
    If search_up is True, all cases and their parent cases are returned.
    If search_up is False, all cases and their child cases are returned.
    """
    if not initial_case_list:
        return {}

    # todo: should assert that domain exists here but this breaks tests
    case_db = CaseDbCache(domain=domain,
                          strip_history=strip_history,
                          deleted_ok=True)

    def related(case_db, case):
        return [
            case_db.get(index.referenced_id)
            for index in (case.indices if search_up else case.reverse_indices)
        ]

    relevant_cases = {}
    relevant_deleted_case_ids = []

    queue = list(case for case in initial_case_list)
예제 #11
0
 def test(self):
     form = make_form_from_case_blocks([ElementTree.fromstring(CASE_BLOCK)])
     lock_manager = process_xform(form, domain=self.domain)
     with lock_manager as xforms:
         with CaseDbCache(domain=self.domain) as case_db:
             with self.assertRaises(PhoneDateValueError):
                 process_cases_with_casedb(xforms, case_db)
예제 #12
0
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)

    stock_report_helpers, case_actions = get_stock_actions(xform)
    # omitted: normalize_transactions (used for bulk requisitions?)

    # validate product ids
    if any(transaction_helper.product_id in ('', None)
            for stock_report_helper in stock_report_helpers
            for transaction_helper in stock_report_helper.transactions):
        raise MissingProductId(
            _('Product IDs must be set for all ledger updates!'))

    relevant_cases = []
    # touch every case for proper ota restore logic syncing to be preserved
    for case_id, case_action in case_actions:
        case = case_db.get(case_id)
        relevant_cases.append(case)
        if case is None:
            raise IllegalCaseId(
                _('Ledger transaction references invalid Case ID "{}"')
                .format(case_id))

        # 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)
예제 #13
0
    def testDomainCheck(self):
        id = uuid.uuid4().hex
        post_case_blocks([
                CaseBlock(
                    create=True, case_id=id,
                    user_id='some-user', version=V2
                ).as_xml()
            ], {'domain': 'good-domain'}
        )
        bad_cache = CaseDbCache(domain='bad-domain')
        try:
            bad_cache.get(id)
            self.fail('domain security check failed to raise exception')
        except IllegalCaseId:
            pass

        good_cache = CaseDbCache(domain='good-domain')
        case = good_cache.get(id)
        self.assertEqual('some-user', case.user_id) # just sanity check it's the right thing
예제 #14
0
def get_related_cases(initial_case_list, domain, strip_history=False, search_up=True):
    """
    Gets the flat list of related cases based on a starting list.
    Walks all the referenced indexes recursively.
    If search_up is True, all cases and their parent cases are returned.
    If search_up is False, all cases and their child cases are returned.
    """
    if not initial_case_list:
        return {}

    # todo: should assert that domain exists here but this breaks tests
    case_db = CaseDbCache(domain=domain,
                          strip_history=strip_history,
                          deleted_ok=True,
                          initial=initial_case_list)

    def related(case_db, case):
        return [case_db.get(index.referenced_id) for index in (case.indices if search_up else case.reverse_indices)]

    relevant_cases = {}
    relevant_deleted_case_ids = []

    queue = list(case for case in initial_case_list)
    directly_referenced_indices = itertools.chain(*[[index.referenced_id for index in (case.indices if search_up else case.reverse_indices)]
                                                    for case in initial_case_list])
    case_db.populate(directly_referenced_indices)
    while queue:
        case = queue.pop()
        if case and case.case_id not in relevant_cases:
            relevant_cases[case.case_id] = case
            if case.doc_type == 'CommCareCase-Deleted':
                relevant_deleted_case_ids.append(case.case_id)
            queue.extend(related(case_db, case))

    if relevant_deleted_case_ids:
        logging.info('deleted cases included in footprint (restore): %s' % (
            ', '.join(relevant_deleted_case_ids)
        ))
    return relevant_cases
예제 #15
0
    def testDomainCheck(self):
        id = uuid.uuid4().hex
        post_case_blocks([
            CaseBlock(create=True, case_id=id, user_id='some-user',
                      version=V2).as_xml()
        ], {'domain': 'good-domain'})
        bad_cache = CaseDbCache(domain='bad-domain')
        try:
            bad_cache.get(id)
            self.fail('domain security check failed to raise exception')
        except IllegalCaseId:
            pass

        good_cache = CaseDbCache(domain='good-domain')
        case = good_cache.get(id)
        self.assertEqual(
            'some-user',
            case.user_id)  # just sanity check it's the right thing
예제 #16
0
class TestCasesReceivedSignal(TestCase):
    def test_casedb_already_has_cases(self):
        case = CaseFactory().create_case()
        case_db = CaseDbCache(
            initial=[CommCareCase(_id='fake1'),
                     CommCareCase(_id='fake2')])
        form = XFormInstance.get(case.xform_ids[0])

        def assert_exactly_one_case(sender, xform, cases, **kwargs):
            global case_count
            case_count = len(cases)

        cases_received.connect(assert_exactly_one_case)
        try:
            process_cases_with_casedb([form], case_db)
            self.assertEqual(1, case_count)
        finally:
            cases_received.disconnect(assert_exactly_one_case)
예제 #17
0
    def testStripHistory(self):
        case_ids = _make_some_cases(3)

        history_cache = CaseDbCache()
        for i, id in enumerate(case_ids):
            self.assertFalse(history_cache.in_cache(id))
            case = history_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) > 0)

        nohistory_cache = CaseDbCache(strip_history=True)
        for i, id in enumerate(case_ids):
            self.assertFalse(nohistory_cache.in_cache(id))
            case = nohistory_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) == 0)

        more_case_ids = _make_some_cases(3)
        history_cache.populate(more_case_ids)
        nohistory_cache.populate(more_case_ids)

        for i, id in enumerate(more_case_ids):
            self.assertTrue(history_cache.in_cache(id))
            case = history_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) > 0)

        for i, id in enumerate(more_case_ids):
            self.assertTrue(nohistory_cache.in_cache(id))
            case = nohistory_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) == 0)
예제 #18
0
 def test_nowrap(self):
     case_ids = _make_some_cases(1)
     cache = CaseDbCache(wrap=False)
     case = cache.get(case_ids[0])
     self.assertTrue(isinstance(case, dict))
     self.assertFalse(isinstance(case, CommCareCase))
예제 #19
0
    def testStripHistory(self):
        case_ids = _make_some_cases(3)

        history_cache = CaseDbCache()
        for i, id in enumerate(case_ids):
            self.assertFalse(history_cache.in_cache(id))
            case = history_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) > 0)

        nohistory_cache = CaseDbCache(strip_history=True)
        for i, id in enumerate(case_ids):
            self.assertFalse(nohistory_cache.in_cache(id))
            case = nohistory_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) == 0)

        more_case_ids = _make_some_cases(3)
        history_cache.populate(more_case_ids)
        nohistory_cache.populate(more_case_ids)

        for i, id in enumerate(more_case_ids):
            self.assertTrue(history_cache.in_cache(id))
            case = history_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) > 0)

        for i, id in enumerate(more_case_ids):
            self.assertTrue(nohistory_cache.in_cache(id))
            case = nohistory_cache.get(id)
            self.assertEqual(str(i), case.my_index)
            self.assertTrue(len(case.actions) == 0)
예제 #20
0
파일: util.py 프로젝트: ekush/commcare-hq
    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()
예제 #21
0
 def test_nowrap(self):
     case_ids = _make_some_cases(1)
     cache = CaseDbCache(wrap=False)
     case = cache.get(case_ids[0])
     self.assertTrue(isinstance(case, dict))
     self.assertFalse(isinstance(case, CommCareCase))