def validate_state(self): check_version(self.params.version) if self.last_sync_log: if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() exception = BadStateException( server_hash=computed_hash, phone_hash=parsed_hash, case_ids=self.last_sync_log. get_footprint_of_cases_on_phone()) if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED: from corehq.apps.reports.standard.deployments import SyncHistoryReport _assert = soft_assert(to=['czue' + '@' + 'dimagi.com']) sync_history_url = '{}?individual={}'.format( SyncHistoryReport.get_url(self.domain), self.user.user_id) _assert( False, '{}, sync history report: {}'.format( exception, sync_history_url)) raise exception
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() exception = BadStateException( server_hash=computed_hash, phone_hash=parsed_hash, case_ids=self.last_sync_log.get_footprint_of_cases_on_phone() ) if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED: from corehq.apps.reports.standard.deployments import SyncHistoryReport _assert = soft_assert(to=['czue' + '@' + 'dimagi.com']) sync_history_url = '{}?individual={}'.format( SyncHistoryReport.get_url(self.domain), self.user.user_id ) _assert(False, '{}, sync history report: {}'.format(exception, sync_history_url)) raise exception
def validate(self): # runs validation checks, raises exceptions if anything is amiss check_version(self.version) if self.sync_log and self.state_hash: parsed_hash = CaseStateHash.parse(self.state_hash) if self.sync_log.get_state_hash() != parsed_hash: raise BadStateException(expected=self.sync_log.get_state_hash(), actual=parsed_hash, case_ids=self.sync_log.get_footprint_of_cases_on_phone())
def get_case_element(case, updates, version=V1): check_version(version) if case is None: logging.error("Can't generate case xml for empty case!") return "" generator = get_generator(version, case) root = generator.get_root_element() # if creating, the base data goes there, otherwise it goes in the # update block do_create = const.CASE_ACTION_CREATE in updates do_update = const.CASE_ACTION_UPDATE in updates do_index = do_update # NOTE: we may want to differentiate this eventually do_attach = do_update do_purge = const.CASE_ACTION_PURGE in updates or const.CASE_ACTION_CLOSE in updates if do_create: # currently the below code relies on the assumption that # every create also includes an update create_block = generator.get_create_element() generator.add_base_properties(create_block) root.append(create_block) if do_update: update_block = generator.get_update_element() # if we don't have a create block, also put the base properties # in the update block, in case they changed if not do_create: generator.add_base_properties(update_block) # custom properties generator.add_custom_properties(update_block) if list(update_block): root.append(update_block) if do_index: generator.add_indices(root) if do_attach: generator.add_attachments(root) if do_purge: purge_block = generator.get_close_element() root.append(purge_block) if not do_purge: # only send down referrals if the case is not being purged generator.add_referrals(root) return root
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if not isinstance(self.last_sync_log, self.sync_log_class): raise IncompatibleSyncLogType('Unable to convert from {} to {}'.format( type(self.last_sync_log), self.sync_log_class, )) if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: raise BadStateException(expected=computed_hash, actual=parsed_hash, case_ids=self.last_sync_log.get_footprint_of_cases_on_phone())
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() raise BadStateException(server_hash=computed_hash, phone_hash=parsed_hash, case_ids=self.last_sync_log. get_footprint_of_cases_on_phone())
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() raise BadStateException( server_hash=computed_hash, phone_hash=parsed_hash, case_ids=self.last_sync_log.get_footprint_of_cases_on_phone() )
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if (self._case_sync == CLEAN_OWNERS and self.last_sync_log.log_format == LOG_FORMAT_LIVEQUERY): raise RestoreException("clean_owners sync after livequery sync") if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() raise BadStateException( server_hash=computed_hash, phone_hash=parsed_hash, case_ids=self.last_sync_log.get_footprint_of_cases_on_phone() )
def validate_state(self): check_version(self.params.version) if self.last_sync_log: if self.params.state_hash: parsed_hash = CaseStateHash.parse(self.params.state_hash) computed_hash = self.last_sync_log.get_state_hash() if computed_hash != parsed_hash: # log state error on the sync log self.last_sync_log.had_state_error = True self.last_sync_log.error_date = datetime.utcnow() self.last_sync_log.error_hash = str(parsed_hash) self.last_sync_log.save() exception = BadStateException( expected=computed_hash, actual=parsed_hash, case_ids=self.last_sync_log.get_footprint_of_cases_on_phone() ) if self.last_sync_log.log_format == LOG_FORMAT_SIMPLIFIED: _assert = soft_assert(to=['czue' + '@' + 'dimagi.com']) _assert(False, str(exception)) raise exception
def get_case_xml(case, updates, version=V1): check_version(version) return tostring(get_case_element(case, updates, version))
def get_generator(version, case): check_version(version) return GENERATOR_MAP[version](case)
def tostring(element): # save some typing, force UTF-8 return ElementTree.tostring(element, encoding="utf-8") def get_sync_element(restore_id): elem = safe_element("Sync") elem.attrib = {"xmlns": SYNC_XMLNS} elem.append(safe_element("restore_id", restore_id)) return elem def get_case_element(case, updates, version=V1): check_version(version) if case is None: logging.error("Can't generate case xml for empty case!") return "" generator = get_generator(version, case) root = generator.get_root_element() # if creating, the base data goes there, otherwise it goes in the # update block do_create = const.CASE_ACTION_CREATE in updates do_update = const.CASE_ACTION_UPDATE in updates do_index = do_update # NOTE: we may want to differentiate this eventually do_attach = do_update do_purge = const.CASE_ACTION_PURGE in updates or const.CASE_ACTION_CLOSE in updates
def generate_restore_payload(user, restore_id="", version="1.0", state_hash=""): """ Gets an XML payload suitable for OTA restore. If you need to do something other than find all cases matching user_id = user.user_id then you have to pass in a user object that overrides the get_case_updates() method. It should match the same signature as models.user.get_case_updates(): user: who the payload is for. must implement get_case_updates restore_id: sync token version: the CommCare version returns: the xml payload of the sync operation """ check_version(version) last_sync = None if restore_id: try: last_sync = SyncLog.get(restore_id) except Exception: logging.error("Request for bad sync log %s by %s, ignoring..." % (restore_id, user)) if last_sync and state_hash: parsed_hash = CaseStateHash.parse(state_hash) if last_sync.get_state_hash() != parsed_hash: raise BadStateException(expected=last_sync.get_state_hash(), actual=parsed_hash, case_ids=last_sync.get_footprint_of_cases_on_phone()) sync_operation = user.get_case_updates(last_sync) case_xml_elements = [xml.get_case_element(op.case, op.required_updates, version) \ for op in sync_operation.actual_cases_to_sync] last_seq = get_db().info()["update_seq"] # create a sync log for this previous_log_id = last_sync.get_id if last_sync else None synclog = SyncLog(user_id=user.user_id, last_seq=last_seq, owner_ids_on_phone=user.get_owner_ids(), date=datetime.utcnow(), previous_log_id=previous_log_id, cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_owned_cases], dependent_cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_extended_cases]) synclog.save() # start with standard response response = get_response_element( "Successfully restored account %s!" % user.username, ResponseNature.OTA_RESTORE_SUCCESS) # add sync token info response.append(xml.get_sync_element(synclog.get_id)) # registration block response.append(xml.get_registration_element(user)) # fixture block for fixture in generator.get_fixtures(user, version, last_sync): response.append(fixture) # case blocks for case_elem in case_xml_elements: response.append(case_elem) return xml.tostring(response)