diff --git a/oml/changelog.py b/oml/changelog.py index 1ffd4d0..83f5c46 100644 --- a/oml/changelog.py +++ b/oml/changelog.py @@ -108,7 +108,7 @@ class Changelog(db.Model): c.revision = revision c.data = data args = json.loads(data) - logger.debug('apply change from %s: %s', user.name, args) + logger.debug('apply change from %s: %s(%s)', user.name, args[0], args[1:]) if getattr(c, 'action_' + args[0])(user, timestamp, *args[1:]): logger.debug('change applied') state.db.session.add(c) @@ -160,6 +160,7 @@ class Changelog(db.Model): i.modified = ts2datetime(timestamp) if user not in i.users: i.add_user(user) + i.info['_from'] = user.id i.update() return True @@ -167,12 +168,17 @@ class Changelog(db.Model): from user.models import Metadata m = Metadata.get_or_create(user.id, itemid) m.edit(meta) - ''' - FIXME: "sometimes" update item too... + #FIXME: "sometimes" update item too... from item.models import Item i = Item.get(itemid) - i.edit(meta, ts2datetime(timestamp)) - ''' + if i: + update = False + if len(i.users) == 1 and user in i.users: + update = True + if i.info.get('_from') == user.id: + update = True + if update: + i.edit(meta, ts2datetime(timestamp)) return True def action_removeitem(self, user, timestamp, itemid): @@ -194,6 +200,7 @@ class Changelog(db.Model): if name == '': return True l = List.create(user.id, name) + trigger_event('addlist', {'id': l.public_id}) return True def action_editlist(self, user, timestamp, name, new): @@ -202,6 +209,7 @@ class Changelog(db.Model): if 'name' in new: l.name = new['name'] l.save() + trigger_event('editlist', {'id': l.public_id}) return True def action_orderlists(self, user, timestamp, lists): @@ -212,6 +220,7 @@ class Changelog(db.Model): l.index_ = idx l.save() idx += 1 + trigger_event('orderlists', {}) return True def action_removelist(self, user, timestamp, name): @@ -219,6 +228,7 @@ class Changelog(db.Model): l = List.get(user.id, name) if l: l.remove() + trigger_event('removelist', {'id': l.public_id}) return True def action_addlistitems(self, user, timestamp, name, ids): @@ -289,3 +299,155 @@ class Changelog(db.Model): if m and m.timestamp < timestamp: m.reset() return True + + @classmethod + def aggregated_changes(cls, since=None, user_id=None): + from user.models import List + if not user_id: + user_id = settings.USER_ID + qs = cls.query.filter_by(user_id=user_id) + qs = Changelog.query.filter_by(user_id=user_id) + if since: + qs = qs.filter(Changelog.revision>=since) + changes = {} + orderlists = False + for c in qs.order_by('timestamp'): + revision = c.revision + timestamp = c.timestamp + data = json.loads(c.data) + op = data[0] + if op in ('editmeta', 'resetmeta'): + continue + action = changes.setdefault(op, {}) + if op == 'additem': + item_id = data[1] + info = data[2] + action[item_id] = [revision, timestamp, info] + if item_id in changes.get('removeitem', []): + del changes['removeitem'][item_id] + elif op == 'edititem': + item_id = data[1] + meta = data[2] + if not item_id in action: + action[item_id] = [revision, timestamp, meta] + else: + action[item_id][0] = revision + action[item_id][1] = timestamp + action[item_id][2].update(meta) + elif op == 'removeitem': + item_id = data[1] + if item_id in changes.get('additem', []): + del changes['additem'][item_id] + else: + action[item_id] = [revision, timestamp] + if item_id in changes.get('edititem', []): + del changes['edititem'][item_id] + elif op == 'addlist': + list_id = data[1] + if list_id: + ids = data[2] if len(data) > 2 else [] + action[list_id] = [revision, timestamp, ids] + elif op == 'editlist': + old_id = data[1] + new_id = data[2]['name'] + r = revision + if old_id not in changes.get('addlist', []): + action[old_id] = [revision, timestamp, new_id] + r += 1 + for a in ('addlist', 'addlistitems', 'removelistitems'): + if a in changes and old_id in changes[a]: + changes[a][new_id] = changes[a].pop(old_id) + changes[a][new_id][0] = r + elif op == 'orderlists': + orderlists = True + elif op == 'removelist': + list_id = data[1] + if list_id not in changes.get('addlist', []): + action[list_id] = [revision, timestamp] + for a in ('addlist', 'addlistitems', 'removelistitems'): + if a in changes and list_id in changes[a]: + del changes[a][list_id] + elif op == 'addlistitems': + list_id = data[1] + if not list_id: + continue + listitems = data[2] + if list_id not in action: + action[list_id] = [revision, timestamp, []] + action[list_id][0] = revision + action[list_id][1] = timestamp + action[list_id][2] += listitems + #remove from removelistitems! + if list_id in changes.get('remvelistitems', {}): + changes['remvelistitems'][list_id] = [ + i for i in changes['remvelistitems'][list_id] if i not in listitems + ] + elif op == 'removelistitems': + list_id = data[1] + listitems = data[2] + #remove from additemlists + removed = [] + if list_id in changes.get('addlistitems',{}): + removed = [ + i for i in changes['addlistitems'][list_id] if i in listitems + ] + changes['addlistitems'][list_id] = [ + i for i in changes['addlistitems'][list_id] if i not in listitems + ] + #remove remaining items + listitems = [ + i for i in listitems if i not in removed + ] + if listitems: + action[list_id] = [revision, timestamp, listitems] + elif op == 'editusername': + old_name = data[1] + new_name = data[2] + #fixme merge multiple edits + action[old_name] = [revision, timestamp, new_name] + elif op == 'editcontact': + old_contact = data[1] + new_contact = data[2] + #fixme merge multiple edits + action[old_contact] = [revision, timestamp, new_contact] + pass + elif op == 'addpeer': + peer = data[1] + info = data[2] + action[peer] = [timestamp, info] + if peer in changes.get('removepeer', []): + del changes['removepeer'][peer] + elif op == 'removepeer': + peer = data[1] + if peer in changes.get('addpeer', []): + del changes['addpeer'][peer] + else: + action[peer] = [revision, timestamp, []] + elif op == 'editmeta': + pass + elif op == 'resetmeta': + pass + else: + print('unknonw action', data) + _changes = [] + for op in list(changes): + if not changes[op]: + del changes[op] + else: + for id in changes[op]: + data = changes[op][id] + rv = data[0] + ts = data[1] + data = [op, id] + data[2:] + _changes.append([rv, ts, json.dumps(data)]) + _changes.sort(key=lambda change: (change[0], change[1])) + if orderlists: + ids = [l.name for l in List.query.filter_by(user_id=user_id,type='static').order_by('index_') if l.name] + if len(ids) > 1: + _changes.append([-1, timestamp, ['orderlists', ids]]) + if _changes: + r = revision - len(_changes) + 1 + for c in _changes: + c[0] = r + r += 1 + return _changes diff --git a/oml/item/api.py b/oml/item/api.py index 5155e20..04b7889 100644 --- a/oml/item/api.py +++ b/oml/item/api.py @@ -132,7 +132,7 @@ def edit(data): for id in ids: item = models.Item.get(id) if item and item.json()['mediastate'] == 'available': - item.edit(data) + item.edit(data, reset_from=True) response = item.json() edited.append(id) else: diff --git a/oml/item/models.py b/oml/item/models.py index 00af0fd..193cb48 100644 --- a/oml/item/models.py +++ b/oml/item/models.py @@ -273,7 +273,7 @@ class Item(db.Model): 'title' ) - def update_meta(self, data, modified=None): + def update_meta(self, data, modified=None, reset_from=False): update = False record = {} for key in self.meta_keys: @@ -286,6 +286,9 @@ class Item(db.Model): if key not in self.meta_keys: del self.meta[key] update = True + if reset_from and '_from' in self.info: + del self.info['_from'] + update = True if update: self.update(modified) self.save() @@ -295,9 +298,9 @@ class Item(db.Model): if record and user in self.users: Changelog.record_ts(user, modified, 'edititem', self.id, record) - def edit(self, data, modified=None): + def edit(self, data, modified=None, reset_from=False): Scrape.query.filter_by(item_id=self.id).delete() - self.update_meta(data, modified) + self.update_meta(data, modified, reset_from=reset_from) for f in self.files.all(): f.move() diff --git a/oml/node/nodeapi.py b/oml/node/nodeapi.py index 522d3cf..e5be9f8 100644 --- a/oml/node/nodeapi.py +++ b/oml/node/nodeapi.py @@ -23,6 +23,8 @@ def api_pullChanges(remote_id, user_id=None, from_=None, to=None): from_ = from_ or 0 if user_id: return [] + return Changelog.aggregated_changes(from_) + ''' if not user_id: user_id = settings.USER_ID qs = Changelog.query.filter_by(user_id=user_id) @@ -32,6 +34,7 @@ def api_pullChanges(remote_id, user_id=None, from_=None, to=None): qs = qs.filter(Changelog.revision