359 lines
12 KiB
Python
359 lines
12 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import logging
|
|
import urllib
|
|
log = logging.getLogger(__name__)
|
|
|
|
class GroupList(object):
|
|
|
|
def __init__(self):
|
|
self.groups={}
|
|
self.lastChange=False
|
|
|
|
def load(self,data):
|
|
if 'lastChange' in data:
|
|
self.lastChange=data['lastChange']
|
|
if 'groups' in data:
|
|
for g in data['groups']:
|
|
self.groups[g]=Group()
|
|
self.groups[g].load(data['groups'][g])
|
|
|
|
def export(self):
|
|
groups={}
|
|
for uuid in self.groups:
|
|
groups[uuid]=self.groups[uuid].export()
|
|
|
|
return {
|
|
'lastChange': self.lastChange,
|
|
'groups': groups
|
|
}
|
|
|
|
def toJSON(self,pretty=False):
|
|
if pretty:
|
|
return json.dumps(self.export(),indent=4, separators=(',', ': '))
|
|
else:
|
|
return json.dumps(self.export())
|
|
|
|
def sync(self,groups):
|
|
ret=GroupList()
|
|
if groups.lastChange<self.lastChange:
|
|
ret.lastChange=self.lastChange
|
|
else:
|
|
ret.lastChange=groups.lastChange
|
|
for uuid in groups.groups:
|
|
if uuid in self.groups:
|
|
ret.groups[uuid]=self.groups[uuid].sync(groups.groups[uuid])
|
|
else:
|
|
ret.groups[uuid]=groups.groups[uuid]
|
|
for uuid in self.groups:
|
|
if uuid not in ret.groups:
|
|
ret.groups[uuid]=self.groups[uuid]
|
|
return ret
|
|
|
|
|
|
class Group(object):
|
|
|
|
def __init__(self):
|
|
self.uuid=None
|
|
self.name=None
|
|
self.contributors={}
|
|
self.deletedContributors={}
|
|
self.contributions={}
|
|
self.deletedContributions={}
|
|
self.categories={}
|
|
self.deletedCategories={}
|
|
|
|
def load(self,data):
|
|
self.uuid=data.get('uuid',None)
|
|
self.name=data.get('name',None)
|
|
for email in data.get('contributors'):
|
|
self.contributors[email]=Contributor()
|
|
self.contributors[email].load(data['contributors'][email])
|
|
if 'deletedContributors' in data:
|
|
for email in data.get('deletedContributors',{}):
|
|
self.deletedContributors[email]=Contributor()
|
|
self.deletedContributors[email].load(data['deletedContributors'][email])
|
|
for uuid in data.get('contributions',{}):
|
|
self.contributions[uuid]=Contribution()
|
|
self.contributions[uuid].load(data['contributions'][uuid])
|
|
if 'deletedContributions' in data:
|
|
for uuid in data.get('deletedContributions',{}):
|
|
self.deletedContributions[uuid]=Contribution()
|
|
self.deletedContributions[uuid].load(data['deletedContributions'][uuid])
|
|
if 'categories' in data:
|
|
for uuid in data.get('categories',{}):
|
|
self.categories[uuid]=Category()
|
|
self.categories[uuid].load(data['categories'][uuid])
|
|
if 'deletedCategories' in data:
|
|
for uuid in data.get('deletedCategories',{}):
|
|
self.deletedCategories[uuid]=Category()
|
|
self.deletedCategories[uuid].load(data['deletedCategories'][uuid])
|
|
return True
|
|
|
|
def export(self):
|
|
contributors={}
|
|
for email in self.contributors:
|
|
contributors[email]=self.contributors[email].export()
|
|
|
|
deletedContributors={}
|
|
for email in self.deletedContributors:
|
|
deletedContributors[email]=self.deletedContributors[email].export()
|
|
|
|
contributions={}
|
|
for uuid in self.contributions:
|
|
contributions[uuid]=self.contributions[uuid].export()
|
|
|
|
deletedContributions={}
|
|
for uuid in self.deletedContributions:
|
|
deletedContributions[uuid]=self.deletedContributions[uuid].export()
|
|
|
|
categories={}
|
|
for uuid in self.categories:
|
|
categories[uuid]=self.categories[uuid].export()
|
|
|
|
deletedCategories={}
|
|
for uuid in self.deletedCategories:
|
|
deletedCategories[uuid]=self.deletedCategories[uuid].export()
|
|
|
|
return {
|
|
'uuid': self.uuid,
|
|
'name': self.name,
|
|
'contributors': contributors,
|
|
'deletedContributors': deletedContributors,
|
|
'contributions': contributions,
|
|
'deletedContributions': deletedContributions,
|
|
'categories': categories,
|
|
'deletedCategories': deletedCategories
|
|
}
|
|
|
|
def restoreContributor(self, email):
|
|
contributor=Contributor()
|
|
contributor.load(self.deletedContributors[email].export())
|
|
for uuid in self.deletedContributions.keys():
|
|
if self.deletedContributions[uuid].contributor==contributor.email and self.deletedContributions[uuid].lastChange==contributor.deletionTime:
|
|
self.contributions[uuid]=Contribution()
|
|
self.contributions[uuid].load(self.deletedContributions[uuid].export())
|
|
# Restored contribution must not be more up to date than other
|
|
self.contributions[uuid].lastChange=0
|
|
del self.deletedContributions[uuid]
|
|
contributor.deletionTime=None
|
|
self.contributors[email]=contributor
|
|
del self.deletedContributors[email]
|
|
|
|
def deleteContributor(self, email, time):
|
|
contributor=Contributor()
|
|
contributor.load(self.contributors[email].export())
|
|
contributor.deletionTime=time
|
|
for uuid in self.contributions.keys():
|
|
if self.contributions[uuid].contributor==email:
|
|
self.deletedContributions[uuid]=Contribution()
|
|
self.deletedContributions[uuid].load(self.contributions[uuid].export())
|
|
self.deletedContributions[uuid].lastChange=time
|
|
del self.contributions[uuid]
|
|
self.deletedContributors[email]=contributor
|
|
del self.contributors[email]
|
|
|
|
def sync(self, group):
|
|
ret=Group()
|
|
ret.uuid=self.uuid
|
|
|
|
# FIXME : Add lastChange on group to permit name choice between to object
|
|
ret.name=group.name
|
|
|
|
## Deleted Contributors
|
|
for email in self.deletedContributors.keys():
|
|
if email not in group.deletedContributors:
|
|
logging.debug('Contributor %s not deleted on the other' % email)
|
|
lastChange=0
|
|
for uuid in group.contributions:
|
|
if group.contributions[uuid].contributor==email and group.contributions[uuid].lastChange>lastChange:
|
|
lastChange=group.contributions[uuid].lastChange
|
|
if self.deletedContributors[email].deletionTime<lastChange:
|
|
logging.debug('Some modifications are more recent than my deletion, retoring contributors and his contributions')
|
|
# Restore contributor and his contributions
|
|
self.restoreContributor(email)
|
|
continue
|
|
elif email in group.contributors:
|
|
logging.debug('My deletion are more recent than other modification, deleting contributors and his contributions in the other group')
|
|
# Delete contributor and his contributions
|
|
group.deleteContributor(email,self.deletedContributors[email].deletionTime)
|
|
ret.deletedContributors[email]=self.deletedContributors[email]
|
|
|
|
for email in group.deletedContributors.keys():
|
|
if email not in ret.deletedContributors:
|
|
logging.debug('Contributor %s not deleted on me' % email)
|
|
lastChange=0
|
|
for uuid in ret.contributions:
|
|
if ret.contributions[uuid].contributor==email and ret.contributions[uuid].lastChange>lastChange:
|
|
lastChange=ret.contributions[uuid].lastChange
|
|
if group.deletedContributors[email].deletionTime<lastChange:
|
|
logging.debug('Some of my modifications are more recent than the other deletion, retoring contributors and his contributions in the other group')
|
|
# Restore contributor and his contributions
|
|
group.restoreContributor(email)
|
|
continue
|
|
elif email in group.contributors:
|
|
# Delete contributor and his contributions
|
|
logging.debug('The other group deletion are more recent than my modifications, deleting my contributor and his contributions')
|
|
ret.deleteContributor(email,group.deletedContributors[email].deletionTime)
|
|
ret.deletedContributors[email]=group.deletedContributors[email]
|
|
|
|
## Contributors
|
|
for email in self.contributors:
|
|
if email not in ret.deletedContributors:
|
|
ret.contributors[email]=self.contributors[email]
|
|
for email in group.contributors:
|
|
if email not in ret.contributors and email not in ret.deletedContributors:
|
|
ret.contributors[email]=group.contributors[email]
|
|
|
|
## Deleted Contributions
|
|
for uuid in self.deletedContributions:
|
|
if uuid in group.deletedContributions:
|
|
ret.deletedContributions[uuid]=self.deletedContributions[uuid].sync(group.deletedContributions[uuid])
|
|
else:
|
|
ret.deletedContributions[uuid]=self.deletedContributions[uuid]
|
|
for uuid in group.deletedContributions:
|
|
if uuid not in ret.deletedContributions:
|
|
ret.deletedContributions[uuid]=group.deletedContributions[uuid]
|
|
elif ret.deletedContributions[uuid].lastChange<group.deletedContributions[uuid].lastChange:
|
|
ret.deletedContributions[uuid]=group.deletedContributions[uuid]
|
|
|
|
## Contributions
|
|
for uuid in self.contributions:
|
|
if uuid in group.contributions:
|
|
ret.contributions[uuid]=self.contributions[uuid].sync(group.contributions[uuid])
|
|
elif uuid not in ret.deletedContributions:
|
|
ret.contributions[uuid]=self.contributions[uuid]
|
|
elif self.contributions[uuid].lastChange>ret.deletedContributions[uuid].lastChange:
|
|
ret.contributions[uuid]=self.contributions[uuid]
|
|
del ret.deletedContributions[uuid]
|
|
for uuid in group.contributions:
|
|
if uuid not in ret.contributions:
|
|
if uuid not in ret.deletedContributions:
|
|
ret.contributions[uuid]=group.contributions[uuid]
|
|
elif group.contributions[uuid].lastChange>ret.deletedContributions[uuid].lastChange:
|
|
ret.contributions[uuid]=group.contributions[uuid]
|
|
del ret.deletedContributions[uuid]
|
|
|
|
## Categories
|
|
for uuid in self.categories:
|
|
if uuid in group.categories:
|
|
ret.categories[uuid]=self.categories[uuid].sync(group.categories[uuid])
|
|
elif uuid not in ret.deletedCategories:
|
|
ret.categories[uuid]=self.categories[uuid]
|
|
elif self.categories[uuid].lastChange>ret.deletedCategories[uuid].lastChange:
|
|
ret.categories[uuid]=self.categories[uuid]
|
|
del ret.deletedCategories[uuid]
|
|
for uuid in group.categories:
|
|
if uuid not in ret.categories:
|
|
if uuid not in ret.deletedCategories:
|
|
ret.categories[uuid]=group.categories[uuid]
|
|
elif group.categories[uuid].lastChange>ret.deletedCategories[uuid].lastChange:
|
|
ret.categories[uuid]=group.categories[uuid]
|
|
del ret.deletedCategories[uuid]
|
|
|
|
return ret
|
|
|
|
|
|
class Contribution(object):
|
|
|
|
def __init__(self):
|
|
self.uuid=None
|
|
self.contributor=None
|
|
self.title=None
|
|
self.cost=None
|
|
self.date=None
|
|
self.category=None
|
|
self.lastChange=None
|
|
|
|
def load(self,data):
|
|
self.uuid=data.get('uuid',None)
|
|
self.contributor=data.get('contributor',None)
|
|
self.title=data.get('title',None)
|
|
self.cost=data.get('cost',None)
|
|
self.date=data.get('date',None)
|
|
self.category=data.get('category',None)
|
|
self.lastChange=data.get('lastChange',None)
|
|
|
|
def export(self):
|
|
return {
|
|
'uuid': self.uuid,
|
|
'contributor': self.contributor,
|
|
'cost': self.cost,
|
|
'title': self.title,
|
|
'date': self.date,
|
|
'category': self.category,
|
|
'lastChange': self.lastChange
|
|
}
|
|
|
|
def sync(self, c):
|
|
if c.lastChange>self.lastChange:
|
|
return c
|
|
else:
|
|
return self
|
|
|
|
class Contributor(object):
|
|
|
|
def __init__(self):
|
|
self.name=None
|
|
self.email=None
|
|
self.deletionTime=None
|
|
|
|
def load(self,data):
|
|
self.name=data.get('name',None)
|
|
self.email=data.get('email',None)
|
|
self.deletionTime=data.get('deletionTime',None)
|
|
|
|
def export(self):
|
|
ret={
|
|
'name': self.name,
|
|
'email': self.email
|
|
}
|
|
if self.deletionTime is not None:
|
|
ret['deletionTime']=self.deletionTime
|
|
return ret
|
|
|
|
class Category(object):
|
|
|
|
def __init__(self):
|
|
self.name=None
|
|
self.color=None
|
|
self.lastChange=None
|
|
|
|
def load(self,data):
|
|
self.name=data.get('name',None)
|
|
self.color=data.get('color',None)
|
|
self.lastChange=data.get('lastChange',None)
|
|
|
|
def export(self):
|
|
ret={
|
|
'name': self.name,
|
|
'color': self.color,
|
|
'lastChange': self.lastChange
|
|
}
|
|
return ret
|
|
|
|
def sync(self, c):
|
|
if c.lastChange>self.lastChange:
|
|
return c
|
|
else:
|
|
return self
|
|
|
|
if __name__ == '__main__':
|
|
import testdata
|
|
import json
|
|
|
|
data=json.loads(testdata.group_as_json)
|
|
data2=json.loads(testdata.group_as_json2)
|
|
|
|
gl=GroupList()
|
|
gl.load(data2)
|
|
|
|
print gl.toJSON(True)
|
|
|
|
print 'Try sync groups'
|
|
gl2=GroupList()
|
|
gl2.load(data)
|
|
gl_sync=gl.sync(gl2)
|
|
print 'Sync group : %s' % gl_sync
|
|
print gl_sync.toJSON(True)
|