rienafairefr / pynYNAB

a python client for the new YNAB
MIT License
138 stars 12 forks source link

Expected delta and delta don't match #30

Closed goncalossantos closed 7 years ago

goncalossantos commented 7 years ago

I'm using your API to connect to my budget and just add a transaction.

I'm creating it myself like this

class YNAB(object):
    def __init__(self, username, password, budget_name):

        self.username = username
        self.password = password
        self.budget_name = budget_name

        self.client = self.get_client()

    def get_client(self):
        conn = nYnabConnection(self.username, self.password)
        try:
            client = nYnabClient(nynabconnection=conn, budgetname=self.budget_name)
            client.sync()
            return client
        except BudgetNotFound:
            raise InvalidYnabSettings("No Budged with that name")

Then I create and add a transaction like this:

    def create_transaction(self, account_id, payee_id, date, amount, memo, imported_payee):
        transactions = []
        transaction = Transaction(
            entities_account_id=account_id,
            amount=amount,
            date=date,
            entities_payee_id=payee_id,
            imported_date=datetime.now().date(),
            imported_payee=imported_payee,
            memo=memo,
            source="Imported"
        )
        transactions.append(transaction)
        return transactions

    def add_transactions(self, transactions):
        for transaction in transactions:
            self.client.add_transaction(transaction)

This is raising an exception in Client.py here:

        if delta != expected_delta:
            raise WrongPushException(expected_delta, delta)

From the value of delta, it seems that it thinks that everything is new. What am I doing wrong?

rienafairefr commented 7 years ago

Hi Goncalo. I think it works as expected. The safeguard probably prevented from modifying and overwriting all your nYnab data at once. Do you see where to look at the changed_entities that it tries to push and compare it to the one it fetched from the server in the sync?

On Feb 12, 2017 02:25, "Gonçalo Silva Santos" notifications@github.com wrote:

I'm using your API to connect to my budget and just add a transaction.

I'm creating it myself like this

class YNAB(object): def init(self, username, password, budget_name):

    self.username = username
    self.password = password
    self.budget_name = budget_name

    self.client = self.get_client()

def get_client(self):
    conn = nYnabConnection(self.username, self.password)
    try:
        client = nYnabClient(nynabconnection=conn,

budgetname=self.budget_name) client.sync() return client except BudgetNotFound: raise InvalidYnabSettings("No Budged with that name")

Then I create and add a transaction like this:

def create_transaction(self, account_id, payee_id, date, amount,

memo, imported_payee): transactions = [] transaction = Transaction( entities_account_id=account_id, amount=amount, date=date, entities_payee_id=payee_id, imported_date=datetime.now().date(), imported_payee=imported_payee, memo=memo, source="Imported" ) transactions.append(transaction) return transactions

def add_transactions(self, transactions):
    for transaction in transactions:
        self.client.add_transaction(transaction)

This is raising an exception in Client.py here:

    if delta != expected_delta:
        raise WrongPushException(expected_delta, delta)

From the value of delta, it seems that it thinks that everything is new. What am I doing wrong?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rienafairefr/nYNABapi/issues/30, or mute the thread https://github.com/notifications/unsubscribe-auth/AAuS9ZwemO3O3T1D6_TJZ9nk5t6o6ejVks5rbl92gaJpZM4L-W5u .

rienafairefr commented 7 years ago

I've pushed new code for testing stuff, commit c83ff5ef23e3550bac3b10796347468cb84f962c can you checkout that, and try running the test_live python -m unittest discover test_live; thing with your login and maybe a dummy budget without your important data in it ? If the test_roundtrip doesnt pass, it's a problem with my code, if test_add_delete_transation fails, it's a problem with our assumption of the API data messages format.

With my login all tests passes, but you might be part of a subset of users with a new feature.

petrica commented 7 years ago

Hi rienafairefr,

Just hit the same exception: WrongPushException(expected_delta, delta) while trying to create an account:

client = clientfromargs(args)

account = Account(
    account_name="Some account name",
    account_type=AccountTypes.Cash,
    on_budget=True,
    sortable_index=random.randint(-50000, 50000),
)
client.add_account(account, 20, datetime.now())

I have commented out WrongPushException line and the account was created as expected. What to do? :+1:

rienafairefr commented 7 years ago

Well, if you're commenting out the safeguard please be advised that you're risking your data. WrongPush means that the client tries to modify more than what it should modify in theory.

petrica commented 7 years ago

The safeguard is OK, my issue is that my only operation since the initial sync was adding an account. How is it possible to have multiple changes uncommitted if I only changed three to add an account.

rienafairefr commented 7 years ago

This python client is experimental based on reverse engineering, it's an unofficial client, so it's very prone to breaking anytime the nYnab team changes something in their API, the implementation is not perfectly equal to what the javascript on app.youneedabudget.com does anyway. Sorry about that, I'm alway trying to work the kinks out. You can try stepping into the problem to see what the client wants to push, to understand why the modification "add an account" does not generate the right changed_entities, if you get more info don't hesitate to share so we can fix this

petrica commented 7 years ago

I have just started debugging this, though my Python expertise is not that great at the moment and I struggle following parts of the algorithm. It will take some time before I can really contribute to this.

You could extend your technical documentation with the exact comments that you have on this issue. I found this discussion to be very valuable in better understanding what the library does.

Thank you for taking the time to write this library.

rossdargan commented 7 years ago

I'm running into the same issue. After looking at catalog.get_changed_apidict() and budget.get_changed_apidict() it appears that it's treating stuff downloaded as changes (I had 100's of items in both, when all I had done is add 1 transaction).

I'm going to try and dive into the code, but python is pretty new to me!

rossdargan commented 7 years ago

Ok, I created a new budget and account to test this.

After creating a client the delta calculates as 0, After a sync it's 179 After adding a transaction it's 180.

so there is 1 difference which is correct. But the code checks for a delta of 1, not 180.

rienafairefr commented 7 years ago

Hi Ross, thanks for the feedback. I don't have as much time as I would like to work on this project, really sorry. You could check the differences between the received dictionary ( changed_entities parameter to method update_from_api_changed_entities ) and what the app wants to push to the server ( the result of the method get_changed_apidict ) that way we could see if it's something that changed in nynab or if it's a bug my library

rossdargan commented 7 years ago

I'm using this code to test how I think it should work:-

    catalog_changed_entities = ynab_client.catalog.get_changed_apidict()
    budget_changed_entities = ynab_client.budget.get_changed_apidict()

    delta = sum(len(l) for k, l in catalog_changed_entities.items()) + \
        sum(len(l) for k, l in budget_changed_entities.items())

    log.debug(delta)

    ynab_client.catalog.clear_changed_entities()
    ynab_client.budget.clear_changed_entities()

    catalog_changed_entities = ynab_client.catalog.get_changed_apidict()
    budget_changed_entities = ynab_client.budget.get_changed_apidict()

    delta = sum(len(l) for k, l in catalog_changed_entities.items()) + \
        sum(len(l) for k, l in budget_changed_entities.items())

    log.debug(delta)

The debug prints 179 for both.

I believe the call to clear_changed_entities should cause the second output to be 0, but it doesn't.

I need to sort out a new machine for developing this as currently I'm modifying my file, and recreating a docker image so it's just so slow, but I'm not a python dev so I'm way out of my depth!

If that doesn't help you I'll see about finding a python IDE so I can help properly!

rossdargan commented 7 years ago

Ok. My issue is because I was getting pynYNAB via "pynYNAB" in the requirements.txt file. Changing it to -e git://github.com/rossdargan/pynYNAB.git@a07fb0ec38337b335680fb8f9901b956a76a0597#egg=pynYNAB fixed it for me (note I'm using my fork but that has no changes).

I'm totally new to python, but where ever pip gets it from when I don't specify it that way obviously has a far older version with bugs!

rossdargan commented 7 years ago

Note however I am still getting this error, but now my calculations show a delta of 1. So I have no idea why WrongPushException is still getting thrown!

rienafairefr commented 7 years ago

Yeah I guess the pypi version is not up to date with master :-/ Sorry about that, I'll update it. I really need to change the handling of this changed_entities thing, right now it's resource intensive it's saving a snapshot of the data before, then making a diff of the data after modifications, reading all of it, it would be better to handle the changes as they happen and save them. There are some unit tests in the app, did you try running them ?

rossdargan commented 7 years ago

test.py and test2.py ran fine.

I'm not sure how to debug code in python yet, so I'm going to try and come up with a really simple example showing the issue and paste that - hopefully it will show something obvious :/

rossdargan commented 7 years ago

Fairly sure the issue is to do with adding a payee, then a transaction. If I add the new payee, then the transaction I get the error 'WrongPushException'.

If I add a payee, then do a sync, then add the transaction I get this error now:

Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/Client.py", line 46, in wrapped self.push(expected_delta) File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/Client.py", line 163, in push self.push_budget() File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/Client.py", line 139, in push_budget self.budgetClient.push(extra=self.extra_budget) File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/ObjClient.py", line 88, in push sync_data = self.connection.dorequest(request_data, self.opname) File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/utils.py", line 20, in rateLimitedFunction ret = func(*args, **kargs) File "/usr/local/lib/python2.7/dist-packages/pynYNAB-dev-py2.7.egg/pynYNAB/connection.py", line 75, in dorequest raise NYnabConnectionError('Uunrecoverable server error, sorry YNAB') pynYNAB.connection.NYnabConnectionError: Uunrecoverable server error, sorry YNAB

After adding a new payee what should I be doing?

rienafairefr commented 7 years ago

I've added documentation in the wiki, hopefully all questions should be answered. Don't hesitate to open new issue if you still have problems. A 0.5.2 version should be incoming soon if tests pass