Beispiel #1
0
def _handle_duplicate(existing_doc, instance, attachments, process):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain
    and *must* include inline attachments

    """
    conflict_id = existing_doc.get_id
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    if existing_md5 != new_md5:
        # if the form contents are not the same:
        #  - "Deprecate" the old form by making a new document with the same contents
        #    but a different ID and a doc_type of XFormDeprecated
        #  - Save the new instance to the previous document to preserve the ID

        old_id = existing_doc._id
        new_id = XFormInstance.get_db().server.next_uuid()

        multi_lock_manager = process_xform(instance, attachments=attachments,
                                           process=process, _id=new_id)
        [(xform, _)] = multi_lock_manager

        # swap the two documents so the original ID now refers to the new one
        # and mark original as deprecated
        xform._id, existing_doc._id = old_id, new_id
        xform._rev, existing_doc._rev = existing_doc._rev, xform._rev

        # flag the old doc with metadata pointing to the new one
        existing_doc.doc_type = deprecation_type()
        existing_doc.orig_id = old_id

        # and give the new doc server data of the old one and some metadata
        xform.received_on = existing_doc.received_on
        xform.deprecated_form_id = existing_doc._id
        xform.edited_on = datetime.datetime.utcnow()

        multi_lock_manager.append(
            LockManager(existing_doc,
                        acquire_lock_for_xform(old_id))
        )
        return multi_lock_manager
    else:
        # follow standard dupe handling, which simply saves a copy of the form
        # but a new doc_id, and a doc_type of XFormDuplicate
        new_doc_id = uid.new()
        new_form, lock = create_xform(instance, attachments=attachments,
                                      _id=new_doc_id, process=process)

        new_form.doc_type = XFormDuplicate.__name__
        dupe = XFormDuplicate.wrap(new_form.to_json())
        dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
        return MultiLockManager([LockManager(dupe, lock)])
Beispiel #2
0
def _handle_duplicate(existing_doc, instance, attachments, process):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain
    and *must* include inline attachments

    """
    conflict_id = existing_doc.get_id
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    if existing_md5 != new_md5:
        # if the form contents are not the same:
        #  - "Deprecate" the old form by making a new document with the same contents
        #    but a different ID and a doc_type of XFormDeprecated
        #  - Save the new instance to the previous document to preserve the ID

        old_id = existing_doc._id
        new_id = XFormInstance.get_db().server.next_uuid()

        multi_lock_manager = process_xform(instance, attachments=attachments,
                                           process=process, _id=new_id)
        [(xform, _)] = multi_lock_manager

        # swap the two documents so the original ID now refers to the new one
        # and mark original as deprecated
        xform._id, existing_doc._id = old_id, new_id
        xform._rev, existing_doc._rev = existing_doc._rev, xform._rev

        # flag the old doc with metadata pointing to the new one
        existing_doc.doc_type = deprecation_type()
        existing_doc.orig_id = old_id

        # and give the new doc server data of the old one and some metadata
        xform.received_on = existing_doc.received_on
        xform.deprecated_form_id = existing_doc._id
        xform.edited_on = datetime.datetime.utcnow()

        multi_lock_manager.append(
            LockManager(existing_doc,
                        acquire_lock_for_xform(old_id))
        )
        return multi_lock_manager
    else:
        # follow standard dupe handling, which simply saves a copy of the form
        # but a new doc_id, and a doc_type of XFormDuplicate
        new_doc_id = uid.new()
        new_form, lock = create_xform(instance, attachments=attachments,
                                      _id=new_doc_id, process=process)

        new_form.doc_type = XFormDuplicate.__name__
        dupe = XFormDuplicate.wrap(new_form.to_json())
        dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
        return MultiLockManager([LockManager(dupe, lock)])
Beispiel #3
0
 def copy(cls, instance):
     """
     Create a backup copy of a instance object, returning the id of the 
     newly created document
     """
     instance_json = instance.to_json()
     backup_id = uid.new()
     instance_json["_id"] = backup_id 
     instance_json.pop("_rev")
     get_db().save_doc(instance_json)
     return backup_id
Beispiel #4
0
def _handle_duplicate(existing_doc, instance, attachments, process):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain
    and *must* include inline attachments

    """
    conflict_id = existing_doc.get_id
    # compare md5s
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    # if not same:
    # Deprecate old form (including changing ID)
    # to deprecate, copy new instance into a XFormDeprecated
    # todo: save transactionally like the rest of the code
    if existing_md5 != new_md5:
        old_id = existing_doc.get_id
        new_id = XFormInstance.get_db().server.next_uuid()

        multi_lock_manager = process_xform(instance, attachments=attachments,
                                           process=process, _id=new_id)
        [(xform, _)] = multi_lock_manager

        # swap the two documents so the original ID now refers to the new one
        # and mark original as deprecated
        xform._id, existing_doc._id = old_id, new_id
        xform._rev, existing_doc._rev = existing_doc._rev, xform._rev

        existing_doc.doc_type = XFormDeprecated.__name__
        existing_doc.orig_id = old_id

        multi_lock_manager.append(
            LockManager(existing_doc,
                        acquire_lock_for_xform(old_id))
        )
        return multi_lock_manager
    else:
        # follow standard dupe handling
        new_doc_id = uid.new()
        new_form, lock = create_xform(instance, attachments=attachments,
                                      _id=new_doc_id, process=process)

        # create duplicate doc
        # get and save the duplicate to ensure the doc types are set correctly
        # so that it doesn't show up in our reports
        new_form.doc_type = XFormDuplicate.__name__
        dupe = XFormDuplicate.wrap(new_form.to_json())
        dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
        return MultiLockManager([LockManager(dupe, lock)])
Beispiel #5
0
def _handle_id_conflict(instance, attachments, process, domain):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the
    previous form and overwrite it with the new form's contents.
    """

    conflict_id = _extract_id_from_raw_xml(instance)

    # get old document
    existing_doc = XFormInstance.get_db().get(conflict_id, attachments=True)
    assert domain
    if existing_doc.get('domain') != domain\
            or existing_doc.get('doc_type') not in doc_types():
        return process_xform(instance, attachments=attachments,
                             process=process, _id=uid.new())
    else:
        existing_doc = XFormInstance.wrap(existing_doc)
        return _handle_duplicate(existing_doc, instance, attachments, process)
Beispiel #6
0
def _handle_duplicate(existing_doc, instance, attachments, process):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain

    """
    conflict_id = existing_doc.get_id
    # compare md5s
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    # if not same:
    # Deprecate old form (including changing ID)
    # to deprecate, copy new instance into a XFormDeprecated
    if existing_md5 != new_md5:
        doc_copy = XFormInstance.get_db().copy_doc(conflict_id)
        # get the doc back to avoid any potential bigcouch race conditions.
        # r=3 implied by class
        xfd = XFormDeprecated.get(doc_copy['id'])
        xfd.orig_id = conflict_id
        xfd.doc_type = XFormDeprecated.__name__
        xfd.save()

        # after that delete the original document and resubmit.
        XFormInstance.get_db().delete_doc(conflict_id)
        return create_and_lock_xform(instance,
                                     attachments=attachments,
                                     process=process)
    else:
        # follow standard dupe handling
        new_doc_id = uid.new()
        new_form_id, lock = create_xform_from_xml(instance,
                                                  _id=new_doc_id,
                                                  process=process)

        # create duplicate doc
        # get and save the duplicate to ensure the doc types are set correctly
        # so that it doesn't show up in our reports
        dupe = XFormDuplicate.get(new_form_id)
        dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
        dupe.save()
        return LockManager(dupe, lock)
Beispiel #7
0
    def testRandomFunction(self):
        # since this is a random statistical test there is a chance
        # it could readily fail. The threshold was chosen to be
        # what seems to be well out of normal range of firing, but
        # in randomness anything is possible.
        test_size = 100000
        threshold = 0.003
        probabilities = [0, 0.02, 0.5, 0.9, 1]
        for probability in probabilities:
            truecount = 0
            falsecount = 0
            while truecount + falsecount < test_size:
                if predictable_random(uid.new(), probability):
                    truecount += 1
                else:
                    falsecount += 1

            calculated_probability = float(truecount) / float(test_size)
            self.assertTrue(probability - threshold < calculated_probability)
            self.assertTrue(probability + threshold > calculated_probability)
Beispiel #8
0
def _handle_duplicate(existing_doc, instance, attachments, process):
    """
    Handle duplicate xforms and xform editing ('deprecation')

    existing doc *must* be validated as an XFormInstance in the right domain

    """
    conflict_id = existing_doc.get_id
    # compare md5s
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    # if not same:
    # Deprecate old form (including changing ID)
    # to deprecate, copy new instance into a XFormDeprecated
    if existing_md5 != new_md5:
        doc_copy = XFormInstance.get_db().copy_doc(conflict_id)
        # get the doc back to avoid any potential bigcouch race conditions.
        # r=3 implied by class
        xfd = XFormDeprecated.get(doc_copy['id'])
        xfd.orig_id = conflict_id
        xfd.doc_type = XFormDeprecated.__name__
        xfd.save()

        # after that delete the original document and resubmit.
        XFormInstance.get_db().delete_doc(conflict_id)
        return create_and_lock_xform(instance, attachments=attachments,
                                     process=process)
    else:
        # follow standard dupe handling
        new_doc_id = uid.new()
        new_form_id, lock = create_xform_from_xml(instance, _id=new_doc_id,
                                                  process=process)

        # create duplicate doc
        # get and save the duplicate to ensure the doc types are set correctly
        # so that it doesn't show up in our reports
        dupe = XFormDuplicate.get(new_form_id)
        dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
        dupe.save()
        return LockManager(dupe, lock)
Beispiel #9
0
def _handle_id_conflict(instance, attachments, process, domain):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the
    previous form and overwrite it with the new form's contents.
    """

    assert domain
    conflict_id = _extract_id_from_raw_xml(instance)

    existing_doc = XFormInstance.get_db().get(conflict_id, attachments=True)
    if existing_doc.get('domain') != domain or existing_doc.get('doc_type') not in doc_types():
        # the same form was submitted to two domains, or a form was submitted with
        # an ID that belonged to a different doc type. these are likely developers
        # manually testing or broken API users. just resubmit with a generated ID.
        return process_xform(instance, attachments=attachments,
                             process=process, _id=uid.new())
    else:
        # It looks like a duplicate/edit in the same domain so pursue that workflow.
        existing_doc = XFormInstance.wrap(existing_doc)
        return _handle_duplicate(existing_doc, instance, attachments, process)
Beispiel #10
0
def _handle_id_conflict(instance, attachments, process, domain):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the
    previous form and overwrite it with the new form's contents.
    """

    assert domain
    conflict_id = _extract_id_from_raw_xml(instance)

    existing_doc = XFormInstance.get_db().get(conflict_id, attachments=True)
    if existing_doc.get('domain') != domain or existing_doc.get('doc_type') not in doc_types():
        # the same form was submitted to two domains, or a form was submitted with
        # an ID that belonged to a different doc type. these are likely developers
        # manually testing or broken API users. just resubmit with a generated ID.
        return process_xform(instance, attachments=attachments,
                             process=process, _id=uid.new())
    else:
        # It looks like a duplicate/edit in the same domain so pursue that workflow.
        existing_doc = XFormInstance.wrap(existing_doc)
        return _handle_duplicate(existing_doc, instance, attachments, process)
Beispiel #11
0
    def from_xform(cls, doc):
        """
        Create an encounter object from an xform document.
        """

        type = ENCOUNTERS_BY_XMLNS[doc.namespace].type if doc.namespace in ENCOUNTERS_BY_XMLNS else doc.namespace

        visit_date = Encounter.get_visit_date(doc)

        metadata = {}
        if doc.metadata:
            metadata = doc.metadata.to_dict()
        return Encounter(
            _id=uid.new(),
            created=datetime.utcnow(),
            edited=datetime.utcnow(),
            visit_date=visit_date,
            type=type,
            is_deprecated=False,
            _metadata=metadata,
            xform_id=doc["_id"],
        )
Beispiel #12
0
def _handle_id_conflict(instance, attachments, process, domain):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the
    previous form and overwrite it with the new form's contents.
    """

    conflict_id = _extract_id_from_raw_xml(instance)

    # get old document
    existing_doc = XFormInstance.get_db().get(conflict_id)
    assert domain
    if existing_doc.get('domain') != domain\
            or existing_doc.get('doc_type') not in doc_types():
        # exit early
        return create_and_lock_xform(instance,
                                     attachments=attachments,
                                     process=process,
                                     _id=uid.new())
    else:
        existing_doc = XFormInstance.wrap(existing_doc)
        return _handle_duplicate(existing_doc, instance, attachments, process)
Beispiel #13
0
    def testCustomResponse(self):
        template_file = os.path.join(os.path.dirname(__file__), "data", "referral.xml")
        with open(template_file) as f:
            template = f.read()

        c = Client()

        now = datetime.now().date()

        def _post_and_confirm(form, count):
            response = c.post("/phone/post/", data=form, content_type="text/xml")
            self.assertEqual(200, response.status_code)
            self.assertTrue(("<FormsSubmittedToday>%s</FormsSubmittedToday>" % count) in response.content)
            self.assertTrue(("<TotalFormsSubmitted>%s</TotalFormsSubmitted>" % count) in response.content)

        for i in range(5):
            form = template % {"date": date_to_xml_string(now), "uid": uid.new()}
            _post_and_confirm(form, i + 1)

        for i in range(5):
            # make sure past submissions also show up as today
            form = template % {"date": date_to_xml_string(now - timedelta(days=i)), "uid": uid.new()}
            _post_and_confirm(form, i + 6)
Beispiel #14
0
            # if not same:
            # Deprecate old form (including changing ID)
            # to deprecate, copy new instance into a XFormDeprecated
            if existing_md5 != new_md5:
                doc_copy = XFormInstance.get_db().copy_doc(conflict_id)
                xfd = XFormDeprecated.get(doc_copy['id'])
                xfd.orig_id = conflict_id
                xfd.doc_type = XFormDeprecated.__name__
                xfd.save()

                # after that delete the original document and resubmit.
                XFormInstance.get_db().delete_doc(conflict_id)
                return post_xform_to_couch(instance, attachments=attachments)
            else:
                # follow standard dupe handling
                new_doc_id = uid.new()
                response, errors = post_from_settings(instance,
                                                      {"uid": new_doc_id})
                if not _has_errors(response, errors):
                    # create duplicate doc
                    # get and save the duplicate to ensure the doc types are set correctly
                    # so that it doesn't show up in our reports
                    dupe = XFormDuplicate.get(response)
                    dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
                    dupe.save()
                    return dupe
                else:
                    # how badly do we care about this?
                    raise CouchFormException(
                        "Problem POSTing form to couch! errors/response: %s/%s"
                        % (errors, response))
Beispiel #15
0
 def __init__(self, patient, pregnancy):
     self.patient = patient
     self._pregnancy = pregnancy
     self._id = uid.new()
Beispiel #16
0
             raise
     else:
         raise XFormException("Problem POSTing form to couch! errors/response: %s/%s" % (errors, response))
 except RequestFailed, e:
     if e.status_int == 409:
         # this is an update conflict, i.e. the uid in the form was the same.
         # log it and flag it.
         def _extract_id_from_raw_xml(xml):
             # TODO: this is brittle as hell. Fix.
             _PATTERNS = (r"<uid>(\w+)</uid>", r"<uuid>(\w+)</uuid>")
             for pattern in _PATTERNS:
                 if re.search(pattern, xml): return re.search(pattern, xml).groups()[0]
             logging.error("Unable to find conflicting matched uid in form: %s" % xml)
             return ""
         conflict_id = _extract_id_from_raw_xml(instance)
         new_doc_id = uid.new()
         response, errors = post_from_settings(instance, {"uid": new_doc_id})
         if not _has_errors(response, errors):
             # create duplicate doc
             # get and save the duplicate to ensure the doc types are set correctly
             # so that it doesn't show up in our reports
             dupe = CXFormDuplicate.get(response)
             dupe.release_lock()
             dupe.save()
             return dupe
         else:
             # how badly do we care about this?
             raise XFormException("Problem POSTing form to couch! errors/response: %s/%s" % (errors, response))
         
     else:
         raise
Beispiel #17
0
def _handle_id_conflict(instance, attachments):
    """
    For id conflicts, we check if the files contain exactly the same content,
    If they do, we just log this as a dupe. If they don't, we deprecate the 
    previous form and overwrite it with the new form's contents.
    """
    def _extract_id_from_raw_xml(xml):
        
        # this is the standard openrosa way of doing things
        parsed = etree.XML(xml)
        meta_ns = "http://openrosa.org/jr/xforms"
        val = parsed.find("{%(ns)s}meta/{%(ns)s}instanceID" % \
                          {"ns": meta_ns})
        if val is not None and val.text:
            return val.text
        
        # if we get here search more creatively for some of the older
        # formats
        _PATTERNS = (r"<instanceID>([\w-]+)</instanceID>",
                     r"<uid>([\w-]+)</uid>",
                     r"<uuid>([\w-]+)</uuid>")
        for pattern in _PATTERNS:
            if re.search(pattern, xml): 
                return re.search(pattern, xml).groups()[0]
        
        logging.error("Unable to find conflicting matched uid in form: %s" % xml)
        return ""
    
    conflict_id = _extract_id_from_raw_xml(instance)
    
    # get old document
    existing_doc = XFormInstance.get(conflict_id)

    # compare md5s
    existing_md5 = existing_doc.xml_md5()
    new_md5 = hashlib.md5(instance).hexdigest()

    # if not same:
    # Deprecate old form (including changing ID)
    # to deprecate, copy new instance into a XFormDeprecated
    if existing_md5 != new_md5:
        doc_copy = XFormInstance.get_db().copy_doc(conflict_id)
        # get the doc back to avoid any potential bigcouch race conditions.
        # r=3 implied by class
        xfd = XFormDeprecated.get(doc_copy['id'])
        xfd.orig_id = conflict_id
        xfd.doc_type=XFormDeprecated.__name__
        xfd.save()

        # after that delete the original document and resubmit.
        XFormInstance.get_db().delete_doc(conflict_id)
        return post_xform_to_couch(instance, attachments=attachments)
    else:
        # follow standard dupe handling
        new_doc_id = uid.new()
        response, errors = post_from_settings(instance, {"uid": new_doc_id})
        if not _has_errors(response, errors):
            # create duplicate doc
            # get and save the duplicate to ensure the doc types are set correctly
            # so that it doesn't show up in our reports
            dupe = XFormDuplicate.get(response)
            dupe.problem = "Form is a duplicate of another! (%s)" % conflict_id
            dupe.save()
            return dupe
        else:
            # how badly do we care about this?
            raise CouchFormException("Problem POSTing form to couch! errors/response: %s/%s" % (errors, response))