def testBasicEdit(self): first_file = os.path.join(os.path.dirname(__file__), "data", "duplicate.xml") edit_file = os.path.join(os.path.dirname(__file__), "data", "edit.xml") with open(first_file, "rb") as f: xml_data1 = f.read() with open(edit_file, "rb") as f: xml_data2 = f.read() doc = post_xform_to_couch(xml_data1) self.assertEqual("7H46J37FGH3", doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("", doc.form['vitals']['height']) self.assertEqual("other", doc.form['assessment']['categories']) doc = post_xform_to_couch(xml_data2) self.assertEqual("7H46J37FGH3", doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("100", doc.form['vitals']['height']) self.assertEqual("Edited Baby!", doc.form['assessment']['categories']) doc = XFormDeprecated.view('couchforms/edits', include_docs=True).first() self.assertEqual("7H46J37FGH3", doc.orig_id) self.assertNotEqual("7H46J37FGH3", doc.get_id) self.assertEqual(XFormDeprecated.__name__, doc.doc_type) self.assertEqual("", doc.form['vitals']['height']) self.assertEqual("other", doc.form['assessment']['categories'])
def test_basic_edit(self): xml_data1, xml_data2 = self._get_files() docs = [] doc = post_xform_to_couch(xml_data1) self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("", doc.form["vitals"]["height"]) self.assertEqual("other", doc.form["assessment"]["categories"]) doc.domain = "test-domain" doc.save() doc = post_xform_to_couch(xml_data2, domain="test-domain") self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("100", doc.form["vitals"]["height"]) self.assertEqual("Edited Baby!", doc.form["assessment"]["categories"]) docs.append(doc) doc = XFormDeprecated.view("couchforms/edits", include_docs=True).first() self.assertEqual(self.ID, doc.orig_id) self.assertNotEqual(self.ID, doc.get_id) self.assertEqual(XFormDeprecated.__name__, doc.doc_type) self.assertEqual("", doc.form["vitals"]["height"]) self.assertEqual("other", doc.form["assessment"]["categories"]) self.assertEqual(XFormInstance.get_db().fetch_attachment(doc.get_id, "form.xml"), xml_data1) self.assertEqual(XFormInstance.get_db().fetch_attachment(self.ID, "form.xml"), xml_data2) for doc in docs: doc.delete()
def tearDown(self): try: XFormInstance.get_db().delete_doc(self.ID) except ResourceNotFound: pass deprecated_xforms = XFormDeprecated.view("couchforms/edits", include_docs=True).all() for form in deprecated_xforms: form.delete()
def tearDown(self): deprecated_xforms = XFormDeprecated.view( 'couchforms/edits', include_docs=True, ).all() for form in deprecated_xforms: form.delete() pass
def access_edits(**kwargs): return XFormDeprecated.temp_view({'map': """ function (doc) { //function to reveal prior edits of xforms. if(doc['doc_type'] == "XFormDeprecated") { emit(doc.orig_id, null); } } """}, **kwargs)
def apply_deprecation(cls, existing_xform, new_xform): # swap the revs new_xform._rev, existing_xform._rev = existing_xform._rev, new_xform._rev existing_xform.doc_type = XFormDeprecated.__name__ deprecated = XFormDeprecated.wrap(existing_xform.to_json()) assert not existing_xform.persistent_blobs, "some blobs would be lost" if existing_xform._deferred_blobs: deprecated._deferred_blobs = existing_xform._deferred_blobs.copy() return deprecated, new_xform
def tearDown(self): try: XFormInstance.get_db().delete_doc("7H46J37FGH3") except: pass deprecated_xforms = XFormDeprecated.view( 'couchforms/edits', include_docs=True, ).all() for form in deprecated_xforms: form.delete()
def apply_deprecation(cls, existing_xform, new_xform): # swap the revs new_xform._rev, existing_xform._rev = existing_xform._rev, new_xform._rev existing_xform.doc_type = XFormDeprecated.__name__ deprecated = XFormDeprecated.wrap(existing_xform.to_json()) assert not existing_xform.persistent_blobs, "some blobs would be lost" if existing_xform._deferred_blobs: deprecated._deferred_blobs = existing_xform._deferred_blobs.copy() user_id = (new_xform.auth_context and new_xform.auth_context.get('user_id')) or 'unknown' operation = XFormOperation(user=user_id, date=new_xform.edited_on, operation='edit') new_xform.history.append(operation) return deprecated, new_xform
def test_basic_edit(self): first_file = os.path.join(os.path.dirname(__file__), "data", "duplicate.xml") edit_file = os.path.join(os.path.dirname(__file__), "data", "edit.xml") with open(first_file, "rb") as f: xml_data1 = f.read() with open(edit_file, "rb") as f: xml_data2 = f.read() docs = [] doc = post_xform_to_couch(xml_data1) self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("", doc.form['vitals']['height']) self.assertEqual("other", doc.form['assessment']['categories']) doc.domain = 'test-domain' doc.save() doc = post_xform_to_couch(xml_data2, domain='test-domain') self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("100", doc.form['vitals']['height']) self.assertEqual("Edited Baby!", doc.form['assessment']['categories']) docs.append(doc) doc = XFormDeprecated.view('couchforms/edits', include_docs=True).first() self.assertEqual(self.ID, doc.orig_id) self.assertNotEqual(self.ID, doc.get_id) self.assertEqual(XFormDeprecated.__name__, doc.doc_type) self.assertEqual("", doc.form['vitals']['height']) self.assertEqual("other", doc.form['assessment']['categories']) for doc in docs: doc.delete()
def test_basic_edit(self): xml_data1, xml_data2 = self._get_files() yesterday = datetime.utcnow() - timedelta(days=1) docs = [] doc = post_xform_to_couch(xml_data1) self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("", doc.form['vitals']['height']) self.assertEqual("other", doc.form['assessment']['categories']) doc.domain = self.domain doc.received_on = yesterday # set this back in time to simulate an edit doc.save() doc = post_xform_to_couch(xml_data2, domain=self.domain) self.assertEqual(self.ID, doc.get_id) self.assertEqual("XFormInstance", doc.doc_type) self.assertEqual("100", doc.form['vitals']['height']) self.assertEqual("Edited Baby!", doc.form['assessment']['categories']) docs.append(doc) deprecated_doc = XFormDeprecated.view('couchforms/edits', include_docs=True).first() self.assertEqual(self.ID, deprecated_doc.orig_id) self.assertNotEqual(self.ID, deprecated_doc._id) self.assertEqual(XFormDeprecated.__name__, deprecated_doc.doc_type) self.assertEqual("", deprecated_doc.form['vitals']['height']) self.assertEqual("other", deprecated_doc.form['assessment']['categories']) self.assertEqual(doc.received_on, deprecated_doc.received_on) self.assertEqual(doc.deprecated_form_id, deprecated_doc._id) self.assertTrue(doc.edited_on > doc.received_on) self.assertEqual(XFormInstance.get_db().fetch_attachment(deprecated_doc._id, 'form.xml'), xml_data1) self.assertEqual(XFormInstance.get_db().fetch_attachment(self.ID, 'form.xml'), xml_data2) for doc in docs: doc.delete()
def deprecate_xform(cls, existing_xform, new_xform): # 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_xform._id new_xform = cls.assign_new_id(new_xform) # swap the two documents so the original ID now refers to the new one # and mark original as deprecated new_xform._id, existing_xform._id = old_id, new_xform._id new_xform._rev, existing_xform._rev = existing_xform._rev, new_xform._rev # flag the old doc with metadata pointing to the new one existing_xform.doc_type = deprecation_type() existing_xform.orig_id = old_id # and give the new doc server data of the old one and some metadata new_xform.received_on = existing_xform.received_on new_xform.deprecated_form_id = existing_xform._id new_xform.edited_on = datetime.datetime.utcnow() return XFormDeprecated.wrap(existing_xform.to_json()), new_xform
def get_previous_versions(form_id): form_ = XFormDeprecated.get(form_id) if getattr(form_, 'deprecated_form_id', None): return get_previous_versions(form_.deprecated_form_id) + [form_] else: return [form_]
def apply_deprecation(cls, existing_xform, new_xform): # swap the revs new_xform._rev, existing_xform._rev = existing_xform._rev, new_xform._rev existing_xform.doc_type = XFormDeprecated.__name__ return XFormDeprecated.wrap(existing_xform.to_json()), new_xform
def setUp(self): for form in XFormDeprecated.view('couchforms/edits', include_docs=True).all(): form.delete()
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))
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) 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
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) 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