Skip to content

Commit

Permalink
no message
Browse files Browse the repository at this point in the history
  • Loading branch information
rienafairefr committed Nov 20, 2016
1 parent 464dd85 commit b4120be
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 214 deletions.
72 changes: 41 additions & 31 deletions pynYNAB/Client.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import logging
from contextlib import contextmanager
from datetime import datetime
from functools import wraps

from sqlalchemy import Date
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

from pynYNAB.Entity import obj_from_dict, Base
from pynYNAB.connection import NYnabConnectionError, nYnabConnection
from pynYNAB.roots import Budget, Catalog
from pynYNAB.schema.budget import Payee, Transaction
from pynYNAB.schema.catalog import BudgetVersion
from pynYNAB.schema.Entity import Base
from pynYNAB.schema.budget import Payee, Transaction, Budget
from pynYNAB.schema.catalog import BudgetVersion, Catalog
from pynYNAB.schema.types import NYNAB_GUID, AmountType
from pynYNAB.scripts.config import get_logger
from pynYNAB.utils import chunk

logger = logging.getLogger('pynYNAB')


def clientfromargs(args, reset=False):
return nYnabClient.from_obj(args,reset)
return nYnabClient.from_obj(args, reset)


class BudgetNotFound(Exception):
pass



class nYnabClient(object):
def __init__(self, nynabconnection, budget_name):
self.delta_device_knowledge = 0
Expand All @@ -44,15 +43,15 @@ def __init__(self, nynabconnection, budget_name):
self.current_device_knowledge = {}
self.device_knowledge_of_server = {}

engine = create_engine('sqlite://')
engine = create_engine('sqlite://',echo=True)

Base.metadata.create_all(engine)
self.Session = sessionmaker(bind=engine)

session=self.Session()
session.add(self.catalog)
session.add(self.budget)
session.commit()
self.session = self.Session()
self.session.add(self.catalog)
self.session.add(self.budget)
self.session.commit()

self.first = True
self.sync()
Expand Down Expand Up @@ -93,7 +92,8 @@ def sync(self):
for budget_version in self.catalog.ce_budget_versions:
if budget_version.budget_id == catalogbudget.id:
self.budget_version_id = budget_version.id
self.sync_obj(self.budget, 'syncBudgetData', knowledge=False,extra=dict(calculated_entities_included=False,budget_version_id=self.budget_version_id))
self.sync_obj(self.budget, 'syncBudgetData', knowledge=False,
extra=dict(calculated_entities_included=False, budget_version_id=self.budget_version_id))
if self.budget_version_id is None and self.budget_name is not None:
raise BudgetNotFound()
else:
Expand All @@ -105,35 +105,45 @@ def sync(self):
else:
self.delta_device_knowledge = 0

self.sync_obj(self.catalog, 'syncCatalogData',extra=dict(user_id="fbec95c7-9fd2-415e-9365-7c4a8e613a49"))
self.sync_obj(self.budget, 'syncBudgetData',extra=dict(calculated_entities_included=False,budget_version_id=self.budget_version_id))
self.sync_obj(self.catalog, 'syncCatalogData', extra=dict(user_id="fbec95c7-9fd2-415e-9365-7c4a8e613a49"))
self.sync_obj(self.budget, 'syncBudgetData',
extra=dict(calculated_entities_included=False, budget_version_id=self.budget_version_id))
self.session.commit()

def update_from_sync_data(self,obj,sync_data):
session=self.Session()
def update_from_sync_data(self, obj, sync_data):
for namefield in obj.ListFields:
getattr(obj, namefield).changed = []
for name, value in sync_data['changed_entities'].items():
if isinstance(value, list):
list_of_entities = getattr(obj, name)
for entityDict in value:
new_obj = session.query(obj.ListFields[name]).get(entityDict['id'])
for column in obj.ListFields[name].__table__.columns:
if column.name in entityDict and entityDict[column.name] is not None:
if column.type.__class__.__name__ == Date.__name__:
entityDict[column.name] = datetime.strptime(entityDict[column.name], '%Y-%m-%d').date()
if column.type.__class__.__name__ == NYNAB_GUID.__name__:
entityDict[column.name] = entityDict[column.name].split('/')[-1]
if column.type.__class__.__name__ == AmountType.__name__:
entityDict[column.name] /= 100

new_obj = self.session.query(obj.ListFields[name]).get(entityDict['id'])
if new_obj is not None:
if 'is_tombstone' in entityDict and entityDict['is_tombstone']:
session.delete(new_obj)
self.session.delete(new_obj)
else:
if new_obj not in list_of_entities:
list_of_entities.append(new_obj)
else:
new_obj.__dict__.update(entityDict)
else:
new_obj = obj.ListFields[name](**entityDict)
session.add(new_obj)
list_of_entities.append(obj)
session.flush()

session.commit()
new_obj = obj.ListFields[name](**entityDict)
self.session.add(new_obj)
list_of_entities.append(new_obj)
new_obj.parent = obj
self.session.flush()

def sync_obj(self, obj, opname,knowledge=True,extra=None):
def sync_obj(self, obj, opname, knowledge=True, extra=None):
if extra is None:
extra = {}
if opname not in self.current_device_knowledge:
Expand All @@ -143,16 +153,16 @@ def sync_obj(self, obj, opname,knowledge=True,extra=None):
if knowledge:
changed_entities = obj.get_changed_entities()
else:
changed_entities={}
changed_entities = {}
# sync with disregard for knowledge, start from 0
request_data = dict(starting_device_knowledge=self.current_device_knowledge[opname],
ending_device_knowledge=self.current_device_knowledge[opname],
device_knowledge_of_server=self.device_knowledge_of_server[opname],
changed_entities={})
ending_device_knowledge=self.current_device_knowledge[opname],
device_knowledge_of_server=self.device_knowledge_of_server[opname],
changed_entities={})
request_data.update(extra)

sync_data = self.connection.dorequest(request_data, opname)
self.update_from_sync_data(obj,sync_data)
self.update_from_sync_data(obj, sync_data)

server_knowledge_of_device = sync_data['server_knowledge_of_device']
current_server_knowledge = sync_data['current_server_knowledge']
Expand Down
2 changes: 1 addition & 1 deletion pynYNAB/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import requests
from requests.cookies import RequestsCookieJar

from pynYNAB.Entity import ComplexEncoder
from pynYNAB.schema.Entity import ComplexEncoder
from pynYNAB.utils import RateLimited


Expand Down
110 changes: 96 additions & 14 deletions pynYNAB/Entity.py → pynYNAB/schema/Entity.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import copy
import json
import logging
from uuid import UUID

import functools
from aenum import Enum
from sqlalchemy import Column
from sqlalchemy import event
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr

from pynYNAB import KeyGenerator
from pynYNAB.schema.Fields import EntityField, EntityListField, PropertyField
from pynYNAB.schema.types import GUID
from pynYNAB.schema.types import NYNAB_GUID

logger = logging.getLogger('pynYNAB')
from sqlalchemy import inspect
Expand Down Expand Up @@ -53,12 +57,9 @@ class AccountTypes(Enum):
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Entity):
pretreated = {}
for namefield, field in obj.AllFields.items():
value = getattr(obj, namefield)
if value != undef:
pretreated[namefield] = field.pretreat(value)
return pretreated
return obj.getdict()
elif isinstance(obj,UUID):
return str(obj)
elif isinstance(obj, ListofEntities):
return list(obj)
elif obj == undef:
Expand Down Expand Up @@ -107,20 +108,104 @@ def addprop(inst, name, method, setter=None, cleaner=None):
return p


class Entity(object):
class BaseModel(object):
id = Column(NYNAB_GUID, primary_key=True, default=KeyGenerator.generateuuid)

@declared_attr
def __tablename__(cls):
return cls.__name__.lower()

id = Column(GUID, primary_key=True, default=KeyGenerator.generateuuid)

class Changed(BaseModel):
pass


class Entity(BaseModel):
def getdict(self):
return self.__dict__


changed = {}

class Root(BaseModel):
@property
def ListFields(self):
relations = inspect(self.__class__).relationships
return {k:relations[k].mapper.class_ for k in relations.keys()}

@property
def ScalarFields(self):
scalarcolumns = self.__table__.columns
return {k: scalarcolumns[k].type.__class__.__name__ for k in scalarcolumns.keys()}

def get_changed_entities(self):
return {key: value for key,value in self.changed.items() if value !=[]}

def clear_changed_entities(self):
self.__changed = {k: [] for k in self.ListFields}

def getdict(self):
objs_dict = {}
for key in self.ListFields:
objs_dict[key] = []
for instance in getattr(self,key):
objs_dict[key].append(instance.getdict())


def ensure_changed(instance,key):
try:
changed_list = changed[instance.id][key]
except:
changed[instance.id] = {}
changed[instance.id] = {}
changed[instance.id][key] = []
changed_list = changed[instance.id][key]


def configure_entity_listener(class_, key, inst):
def set_(instance, value, oldvalue, initiator):
if not hasattr(instance,'parent'):
return
parent = instance.parent
if parent is not None:
for relationship in parent.__mapper__.relationships:
if relationship.mapper == instance.__mapper__:

changed_list.append(instance)

event.listen(inst, 'set', set_)


def configure_listener(class_, key, inst):
def append(instance, value, initiator):
if not instance.is_root:
return
if instance.changed is None:
instance.changed = class_()
entities_list = getattr(instance.changed, initiator.key)
entities_list.append(value)

def remove(instance, value, initiator):
if not instance.is_root:
return
if instance.changed is None:
instance.changed = class_()
value.is_tombstone=True
entities_list = getattr(instance.changed, initiator.key)
entities_list.append(value)

event.listen(inst, 'append', append)
event.listen(inst, 'remove', remove)

#for relationship in class_.__mapper__.relationships:
# remote_class = getattr(class_,relationship.key).class_
# event.listen(remote_class,'attribute_instrument',configure_entity_listener)

Base = declarative_base(cls=Entity)

event.listen(Root, 'attribute_instrument', configure_listener)
event.listen(Entity, 'attribute_instrument', configure_entity_listener)

class EntityCls(object):
def __init__(self, *args, **kwargs):
self.ListFields = {}
Expand Down Expand Up @@ -181,12 +266,9 @@ def __repr__(self):
def __unicode__(self):
return self.__str__()

def getdict(self):
return {namefield: getattr(self, namefield) for namefield in self.AllFields}

def get_changed_entities(self):
firstrun = {namefield: getattr(self, namefield).get_changed_entities() for namefield in self.ListFields}
return {namefield: value for namefield, value in firstrun.items() if value != []}



def update_from_changed_entities(self, changed_entities):
for namefield in self.ListFields:
Expand Down
8 changes: 4 additions & 4 deletions pynYNAB/schema/Fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, typearg):
self.type = typearg

def __call__(self, *args, **kwargs):
from pynYNAB.Entity import ListofEntities
from pynYNAB.schema.Entity import ListofEntities
return ListofEntities(self.type)


Expand Down Expand Up @@ -74,7 +74,7 @@ def pretreat(self, x):
try:
return [self.d.pretreat(i) for i in x]
except AttributeError:
from pynYNAB.Entity import AccountTypes
from pynYNAB.schema.Entity import AccountTypes
if x in AccountTypes:
return x

Expand All @@ -90,12 +90,12 @@ def pretreat(self, x):
try:
return x.name
except AttributeError:
from pynYNAB.Entity import AccountTypes
from pynYNAB.schema.Entity import AccountTypes
if x in AccountTypes:
return x

def posttreat(self, x):
from pynYNAB.Entity import AccountTypes
from pynYNAB.schema.Entity import AccountTypes
try:
return getattr(AccountTypes, x)
except:
Expand Down
Loading

0 comments on commit b4120be

Please sign in to comment.