def conflicts_resolve(db, c, dump_dir=None): """ Conflict resolution algorithm """ changed = False ctender = c[u'doc'] tid = c[u'id'] trev = ctender[u'_rev'] conflicts = ctender[u'_conflicts'] if dump_dir: with open('{}@{}_conflicts.json'.format(os.path.join(dump_dir, tid), trev), 'w') as f: json.dump(ctender, f) update_journal_handler_params({ 'TENDER_ID': tid, 'TENDERID': ctender.get(u'tenderID', ''), 'TENDER_REV': trev, 'RESULT': trev, 'PARAMS': ','.join(conflicts), }) LOGGER.info("Conflict detected", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_detected'}) if 'revisions' in ctender: open_revs = dict([(i, None) for i in conflicts]) #open_revs[trev] = sorted(set([i.get('rev') for i in ctender['revisions']]), key=sort_key) #open_revs[trev] = [i.get('rev') for i in ctender['revisions']] open_revs[trev] = [(i.get('rev'), i['date']) for i in ctender['revisions']] td = {trev: ctender} for r in conflicts: try: t = db.get(tid, rev=r) except ServerError: update_journal_handler_params({'PARAMS': r}) LOGGER.error("ServerError on getting revision", extra={'tenderid': tid, 'rev': r, 'MESSAGE_ID': 'conflict_error_get'}) return if dump_dir: with open('{}@{}.json'.format(os.path.join(dump_dir, tid), r), 'w') as f: json.dump(t, f) #open_revs[r] = sorted(set([i.get('rev') for i in t['revisions']]), key=sort_key) #open_revs[r] = [i.get('rev') for i in t['revisions']] open_revs[r] = [(i.get('rev'), i['date']) for i in t['revisions']] if r not in td: td[r] = t.copy() #common_rev = [i[0] for i in zip(*open_revs.values()) if all(map(lambda x: i[0]==x, i))][-1] common_chain = [i[0] for i in zip(*open_revs.values()) if all(map(lambda x: i[0]==x, i))] try: common_rev = common_chain[-1][0] except IndexError: LOGGER.error("Can't find common revision", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_common'}) return common_index = len(common_chain) applied = [rev['date'] for rev in ctender['revisions'][common_index:]] for r in conflicts: tt = [] t = td[r] revs = t['revisions'] #common_index = [i.get('rev') for i in revs].index(common_rev) for rev in revs[common_index:][::-1]: if 'changes' not in rev: continue tn = t.copy() try: t = _apply_patch(t, rev['changes']) except JsonPatchConflict: LOGGER.error("Can't restore revision", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_restore'}) return ti = dict([x for x in t.items() if x[0] not in IGNORE]) tj = dict([x for x in tn.items() if x[0] not in IGNORE]) tt.append((rev['date'], rev, get_revision_changes(ti, tj))) for i in tt[::-1]: if i[0] in applied: continue t = ctender.copy() try: ctender.update(_apply_patch(t, i[2])) except JsonPointerException: LOGGER.error("Can't apply patch", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_pointer'}) return except JsonPatchConflict: LOGGER.error("Can't apply patch", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_patch'}) return patch = get_revision_changes(ctender, t) if patch: revision = i[1] revision['changes'] = patch revision['rev'] = common_rev ctender['revisions'].append(revision) applied.append(i[0]) changed = True if changed: ctender['dateModified'] = get_now().isoformat() try: tid, trev = db.save(ctender) except ServerError: LOGGER.error("ServerError on saving resolution", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_save'}) return except ResourceConflict: LOGGER.info("Conflict not resolved", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_not_resolved'}) return else: update_journal_handler_params({'RESULT': trev}) LOGGER.info("Conflict resolved", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_resolved'}) else: LOGGER.info("Conflict resolved w/o changes", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_resolved_wo_changes'}) uu=[] for r in conflicts: uu.append({'_id': tid, '_rev': r, '_deleted': True}) try: results = db.update(uu) except ServerError: LOGGER.error("ServerError on deleting conflicts", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_deleting'}) return else: update_journal_handler_params({'TENDER_REV': trev, 'RESULT': ','.join([str(x[0]) for x in results])}) LOGGER.info("Deleting conflicts", extra={'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_deleting'})
def conflicts_resolve(db, c, dump_dir=None): """ Conflict resolution algorithm """ changed = False ctender = c[u'doc'] tid = c[u'id'] trev = ctender[u'_rev'] conflicts = ctender[u'_conflicts'] if dump_dir: with open( '{}@{}_conflicts.json'.format(os.path.join(dump_dir, tid), trev), 'w') as f: json.dump(ctender, f) update_journal_handler_params({ 'TENDER_ID': tid, 'TENDERID': ctender.get(u'tenderID', ''), 'TENDER_REV': trev, 'RESULT': trev, 'PARAMS': ','.join(conflicts), }) LOGGER.info("Conflict detected", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_detected' }) if 'revisions' in ctender: open_revs = dict([(i, None) for i in conflicts]) open_revs[trev] = [(i.get('rev'), i['date']) for i in ctender['revisions']] td = {trev: ctender} for r in conflicts: try: t = db.get(tid, rev=r) except ServerError: update_journal_handler_params({'PARAMS': r}) LOGGER.error("ServerError on getting revision", extra={ 'tenderid': tid, 'rev': r, 'MESSAGE_ID': 'conflict_error_get' }) return if dump_dir: with open('{}@{}.json'.format(os.path.join(dump_dir, tid), r), 'w') as f: json.dump(t, f) open_revs[r] = [(i.get('rev'), i['date']) for i in t['revisions']] if r not in td: td[r] = t.copy() common_chain = [ i[0] for i in zip(*open_revs.values()) if all(map(lambda x: i[0] == x, i)) ] try: common_rev = common_chain[-1][0] except IndexError: LOGGER.error("Can't find common revision", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_common' }) return common_index = len(common_chain) applied = [rev['date'] for rev in ctender['revisions'][common_index:]] for r in conflicts: tt = [] t = td[r] revs = t['revisions'] for rev in revs[common_index:][::-1]: if 'changes' not in rev: continue tn = t.copy() try: t = _apply_patch(t, rev['changes']) except JsonPatchConflict: LOGGER.error("Can't restore revision", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_restore' }) return ti = dict([x for x in t.items() if x[0] not in IGNORE]) tj = dict([x for x in tn.items() if x[0] not in IGNORE]) tt.append((rev['date'], rev, get_revision_changes(ti, tj))) for i in tt[::-1]: if i[0] in applied: continue t = ctender.copy() try: ctender = _apply_patch(t, i[2]) except JsonPointerException: LOGGER.error("Can't apply patch", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_pointer' }) return except JsonPatchConflict: LOGGER.error("Can't apply patch", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_patch' }) return patch = get_revision_changes(ctender, t) if patch: revision = i[1] revision['changes'] = patch revision['rev'] = common_rev ctender['revisions'].append(revision) applied.append(i[0]) changed = True if changed: ctender['dateModified'] = get_now().isoformat() try: tid, trev = db.save(ctender) except ServerError: LOGGER.error("ServerError on saving resolution", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_save' }) return except ResourceConflict: LOGGER.info("Conflict not resolved", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_not_resolved' }) return else: update_journal_handler_params({'RESULT': trev}) LOGGER.info("Conflict resolved", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_resolved' }) else: LOGGER.info("Conflict resolved w/o changes", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_resolved_wo_changes' }) uu = [] for r in conflicts: uu.append({'_id': tid, '_rev': r, '_deleted': True}) try: results = db.update(uu) except ServerError: LOGGER.error("ServerError on deleting conflicts", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_error_deleting' }) return else: update_journal_handler_params({ 'TENDER_REV': trev, 'RESULT': ','.join([str(x[0]) for x in results]) }) LOGGER.info("Deleting conflicts", extra={ 'tenderid': tid, 'rev': trev, 'MESSAGE_ID': 'conflict_deleting' })
def apply_data_patch(item, changes): patch_changes = [] prepare_patch(patch_changes, item, changes) if not patch_changes: return {} return _apply_patch(item, patch_changes)