def handle(self, domain, **options): NotAllowed.check(domain) if not options['noinput']: confirm = input( """ Are you sure you want to hard delete all forms and cases in domain "{}"? This operation is PERMANENT. Type the domain's name again to continue, or anything else to cancel: """.format(domain) ) if confirm != domain: print("\n\t\tDomain deletion cancelled.") return logger.info('Hard deleting forms...') deleted_sql_form_ids = FormAccessorSQL.get_deleted_form_ids_in_domain(domain) for form_id_chunk in chunked(with_progress_bar(deleted_sql_form_ids, stream=silence_during_tests()), 500): FormAccessorSQL.hard_delete_forms(domain, list(form_id_chunk), delete_attachments=True) logger.info('Hard deleting cases...') deleted_sql_case_ids = CaseAccessorSQL.get_deleted_case_ids_in_domain(domain) for case_id_chunk in chunked(with_progress_bar(deleted_sql_case_ids, stream=silence_during_tests()), 500): CaseAccessorSQL.hard_delete_cases(domain, list(case_id_chunk)) logger.info('Done.')
def tag_cases_as_deleted_and_remove_indices(domain, case_ids, deletion_id, deletion_date): from corehq.apps.sms.tasks import delete_phone_numbers_for_owners from corehq.messaging.scheduling.tasks import delete_schedule_instances_for_cases NotAllowed.check(domain) CaseAccessors(domain).soft_delete_cases(list(case_ids), deletion_date, deletion_id) _remove_indices_from_deleted_cases_task.delay(domain, case_ids) delete_phone_numbers_for_owners.delay(case_ids) delete_schedule_instances_for_cases.delay(domain, case_ids)
def handle(self, domain, case_id, **options): NotAllowed.check(domain) case_accessor = CaseAccessors(domain=domain) case = case_accessor.get_case(case_id) if not case.is_deleted and input('\n'.join([ 'Case {} is not already deleted. Are you sure you want to delete it? (y/N)' .format(case_id) ])).lower() != 'y': sys.exit(0)
def soft_delete_forms(domain, form_ids, deletion_date=None, deletion_id=None): def _form_delete(doc): doc['server_modified_on'] = json_format_datetime(datetime.utcnow()) NotAllowed.check(domain) return _soft_delete(XFormInstance.get_db(), form_ids, deletion_date, deletion_id, _form_delete)
def ensure_prerequisites(self, domain, app_id, version_number, test_run): NotAllowed.check(domain) self.domain = domain self.app_id = app_id self.version_number = version_number self.test_run = test_run == 'yes' _notify_parsed_args(domain, app_id, version_number, test_run) app = Application.get(self.app_id) if app.domain != self.domain: raise CommandError('Domain not same as from app id') self.setup()
def delete_exploded_cases(domain, explosion_id, task=None): NotAllowed.check(domain) if task: DownloadBase.set_progress(delete_exploded_case_task, 0, 0) query = (CaseSearchES() .domain(domain) .case_property_query("cc_explosion_id", explosion_id)) case_ids = query.values_list('_id', flat=True) if task: DownloadBase.set_progress(delete_exploded_case_task, 0, len(case_ids)) case_accessor = CaseAccessors(domain) form_accessor = FormAccessors(domain) ledger_accessor = LedgerAccessorSQL deleted_form_ids = set() num_deleted_ledger_entries = 0 for id in case_ids: ledger_form_ids = {tx.form_id for tx in ledger_accessor.get_ledger_transactions_for_case(id)} for form_id in ledger_form_ids: ledger_accessor.delete_ledger_transactions_for_form([id], form_id) num_deleted_ledger_entries += ledger_accessor.delete_ledger_values(id) new_form_ids = set(case_accessor.get_case_xform_ids(id)) - deleted_form_ids form_accessor.soft_delete_forms(list(new_form_ids)) deleted_form_ids |= new_form_ids completed = 0 for ids in chunked(case_ids, 100): case_accessor.soft_delete_cases(list(ids)) if task: completed += len(ids) DownloadBase.set_progress(delete_exploded_case_task, completed, len(case_ids)) return { 'messages': [ "Successfully deleted {} cases".format(len(case_ids)), "Successfully deleted {} forms".format(len(deleted_form_ids)), "Successfully deleted {} ledgers".format(num_deleted_ledger_entries), ] }
def _handle_duplicate(new_doc): """ Handle duplicate xforms and xform editing ('deprecation') existing doc *must* be validated as an XFormInstance in the right domain and *must* include inline attachments :returns: A two-tuple: `(<new form>, <duplicate form or None>)` The new form may have a different `form_id` than `new_doc.form_id`. """ interface = FormProcessorInterface(new_doc.domain) conflict_id = new_doc.form_id try: existing_doc = FormAccessors(new_doc.domain).get_with_attachments(conflict_id) except ResourceNotFound: # Original form processing failed but left behind a form doc with no # attachments. It's safe to delete this now since we're going to re-process # the form anyway. from couchforms.models import XFormInstance XFormInstance.get_db().delete_doc(conflict_id) return new_doc, None try: existing_md5 = existing_doc.xml_md5() except MissingFormXml: existing_md5 = None if not existing_doc.is_error: existing_doc.problem = 'Missing form.xml' new_md5 = new_doc.xml_md5() if existing_md5 != new_md5: _soft_assert = soft_assert(to='{}@{}.com'.format('skelly', 'dimagi'), exponential_backoff=False) if new_doc.xmlns != existing_doc.xmlns: # if the XMLNS has changed this probably isn't a form edit # it could be a UUID clash (yes we've had that before) # Assign a new ID to the form and process as normal + notify_admins xform = interface.assign_new_id(new_doc) _soft_assert( False, "Potential UUID clash", { 'incoming_form_id': conflict_id, 'existing_form_id': existing_doc.form_id, 'new_form_id': xform.form_id, 'incoming_xmlns': new_doc.xmlns, 'existing_xmlns': existing_doc.xmlns, 'domain': new_doc.domain, } ) return xform, None else: if existing_doc.is_error and not existing_doc.initial_processing_complete: # edge case from ICDS where a form errors and then future re-submissions of the same # form do not have the same MD5 hash due to a bug on mobile: # see https://dimagi-dev.atlassian.net/browse/ICDS-376 # since we have a new form and the old one was not successfully processed # we can effectively ignore this form and process the new one as normal if not interface.use_sql_domain: new_doc._rev, existing_doc._rev = existing_doc._rev, new_doc._rev interface.assign_new_id(existing_doc) existing_doc.save() return new_doc, None else: # 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 NotAllowed.check(new_doc.domain) existing_doc, new_doc = apply_deprecation(existing_doc, new_doc, interface) return new_doc, existing_doc else: # follow standard dupe handling, which simply saves a copy of the form # but a new doc_id, and a doc_type of XFormDuplicate duplicate = interface.deduplicate_xform(new_doc) return duplicate, existing_doc
def soft_undelete_cases(domain, case_ids): NotAllowed.check(domain) return _soft_undelete(CommCareCase.get_db(), case_ids)
def soft_delete(self): NotAllowed.check(self.domain) self.doc_type += DELETED_SUFFIX self.save()
def _deprecate_old_form(): NotAllowed.check(new_doc.domain) existing, new = apply_deprecation(existing_doc, new_doc, interface) return new, existing