sqlite-export-for-ynab 2.8.0__tar.gz → 2.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlite_export_for_ynab-2.8.0/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.9.0}/PKG-INFO +2 -2
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/README.md +1 -1
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/setup.cfg +1 -1
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/_main.py +39 -86
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0/sqlite_export_for_ynab.egg-info}/PKG-INFO +2 -2
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/tests/_main_test.py +26 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/LICENSE +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/pyproject.toml +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/setup.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__init__.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__main__.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/create-relations.sql +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/drop-relations.sql +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/py.typed +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/dependency_links.txt +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/entry_points.txt +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/requires.txt +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/top_level.txt +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/testing/__init__.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/testing/fixtures.py +0 -0
- {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: SQLite Export for YNAB - Export YNAB Data to SQLite
|
|
5
5
|
Home-page: https://github.com/mxr/sqlite-export-for-ynab
|
|
6
6
|
Author: Max R
|
|
@@ -272,7 +272,7 @@ WITH interest_by_account AS (
|
|
|
272
272
|
SELECT
|
|
273
273
|
plan_id
|
|
274
274
|
, account_name
|
|
275
|
-
, SUM(
|
|
275
|
+
, SUM(amount_currency) AS total
|
|
276
276
|
FROM flat_transactions
|
|
277
277
|
WHERE
|
|
278
278
|
TRUE
|
{sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/_main.py
RENAMED
|
@@ -46,6 +46,8 @@ from sqlite_export_for_ynab import ddl
|
|
|
46
46
|
|
|
47
47
|
if TYPE_CHECKING:
|
|
48
48
|
from collections.abc import AsyncIterator
|
|
49
|
+
from collections.abc import Awaitable
|
|
50
|
+
from collections.abc import Callable
|
|
49
51
|
from collections.abc import Iterator
|
|
50
52
|
from collections.abc import Sequence
|
|
51
53
|
|
|
@@ -603,7 +605,13 @@ async def insert_entries(
|
|
|
603
605
|
if not entries:
|
|
604
606
|
return
|
|
605
607
|
|
|
606
|
-
|
|
608
|
+
async with context.con.cursor() as cur:
|
|
609
|
+
await cur.execute(f"PRAGMA table_info({table})")
|
|
610
|
+
table_columns = {row["name"] async for row in cur}
|
|
611
|
+
|
|
612
|
+
# Ignore any keys the YNAB API returns that aren't columns in the DDL so
|
|
613
|
+
# newly-added API fields don't break the insert.
|
|
614
|
+
entry_keys = tuple(k for k in entries[0] if k in table_columns)
|
|
607
615
|
sql = f"INSERT OR REPLACE INTO {table} ({', '.join(entry_keys + ('plan_id',))}) VALUES ({', '.join('?' * (len(entry_keys) + 1))})"
|
|
608
616
|
|
|
609
617
|
async with context.con.cursor() as cur:
|
|
@@ -634,100 +642,45 @@ async def _get_all_ynab(
|
|
|
634
642
|
async def _get_plan_data(
|
|
635
643
|
context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
|
|
636
644
|
) -> tuple[str, _YnabPlanData]:
|
|
637
|
-
(
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
_get_scheduled_transactions(context, plan_id, lkos, task_id),
|
|
645
|
+
accounts, categories, payees, transactions, scheduled = await asyncio.gather(
|
|
646
|
+
*(
|
|
647
|
+
_get_ynab(context, endpoint, plan_id, lkos, task_id)
|
|
648
|
+
for endpoint in (
|
|
649
|
+
AccountsApi(context.api_client).get_accounts,
|
|
650
|
+
CategoriesApi(context.api_client).get_categories,
|
|
651
|
+
PayeesApi(context.api_client).get_payees,
|
|
652
|
+
TransactionsApi(context.api_client).get_transactions,
|
|
653
|
+
ScheduledTransactionsApi(context.api_client).get_scheduled_transactions,
|
|
654
|
+
)
|
|
655
|
+
)
|
|
649
656
|
)
|
|
650
|
-
transactions, server_knowledge = transactions_serverknowledge
|
|
651
657
|
return (
|
|
652
658
|
plan_id,
|
|
653
659
|
_YnabPlanData(
|
|
654
|
-
accounts=accounts,
|
|
655
|
-
category_groups=categories,
|
|
656
|
-
payees=payees,
|
|
657
|
-
transactions=transactions,
|
|
658
|
-
server_knowledge=server_knowledge,
|
|
659
|
-
scheduled_transactions=scheduled_transactions,
|
|
660
|
+
accounts=accounts.data.accounts,
|
|
661
|
+
category_groups=categories.data.category_groups,
|
|
662
|
+
payees=payees.data.payees,
|
|
663
|
+
transactions=transactions.data.transactions,
|
|
664
|
+
server_knowledge=transactions.data.server_knowledge,
|
|
665
|
+
scheduled_transactions=scheduled.data.scheduled_transactions,
|
|
660
666
|
),
|
|
661
667
|
)
|
|
662
668
|
|
|
663
669
|
|
|
664
670
|
@retry(stop=stop_after_attempt(3))
|
|
665
|
-
async def
|
|
666
|
-
context: _Context,
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
) -> list[CategoryGroupWithCategories]:
|
|
679
|
-
resp = await CategoriesApi(context.api_client).get_categories(
|
|
680
|
-
plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
|
|
681
|
-
)
|
|
682
|
-
context.progress.update(task_id, advance=1)
|
|
683
|
-
return resp.data.category_groups
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
@retry(stop=stop_after_attempt(3))
|
|
687
|
-
async def _get_payees(
|
|
688
|
-
context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
|
|
689
|
-
) -> list[Payee]:
|
|
690
|
-
resp = await PayeesApi(context.api_client).get_payees(
|
|
691
|
-
plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
|
|
692
|
-
)
|
|
693
|
-
context.progress.update(task_id, advance=1)
|
|
694
|
-
return resp.data.payees
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
@retry(stop=stop_after_attempt(3))
|
|
698
|
-
async def _get_transactions(
|
|
699
|
-
context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
|
|
700
|
-
) -> tuple[list[TransactionDetail], int]:
|
|
701
|
-
resp = await TransactionsApi(context.api_client).get_transactions(
|
|
702
|
-
plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
|
|
703
|
-
)
|
|
704
|
-
context.progress.update(task_id, advance=1)
|
|
705
|
-
return resp.data.transactions, resp.data.server_knowledge
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
@retry(stop=stop_after_attempt(3))
|
|
709
|
-
async def _get_scheduled_transactions(
|
|
710
|
-
context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
|
|
711
|
-
) -> list[ScheduledTransactionDetail]:
|
|
712
|
-
resp = await ScheduledTransactionsApi(
|
|
713
|
-
context.api_client
|
|
714
|
-
).get_scheduled_transactions(
|
|
715
|
-
plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
|
|
716
|
-
)
|
|
717
|
-
context.progress.update(task_id, advance=1)
|
|
718
|
-
return resp.data.scheduled_transactions
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
# @retry(stop=stop_after_attempt(3))
|
|
722
|
-
# async def _get_ynab[T](
|
|
723
|
-
# context: _Context,
|
|
724
|
-
# getter: Callable[..., Awaitable[T]],
|
|
725
|
-
# task_id: TaskID,
|
|
726
|
-
# ) -> T:
|
|
727
|
-
# try:
|
|
728
|
-
# return await getter()
|
|
729
|
-
# finally:
|
|
730
|
-
# context.progress.update(task_id, advance=1)
|
|
671
|
+
async def _get_ynab[T](
|
|
672
|
+
context: _Context,
|
|
673
|
+
endpoint: Callable[..., Awaitable[T]],
|
|
674
|
+
plan_id: str,
|
|
675
|
+
lkos: dict[str, int],
|
|
676
|
+
task_id: TaskID,
|
|
677
|
+
) -> T:
|
|
678
|
+
try:
|
|
679
|
+
return await endpoint(
|
|
680
|
+
plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
|
|
681
|
+
)
|
|
682
|
+
finally:
|
|
683
|
+
context.progress.update(task_id, advance=1)
|
|
731
684
|
|
|
732
685
|
|
|
733
686
|
def main(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: SQLite Export for YNAB - Export YNAB Data to SQLite
|
|
5
5
|
Home-page: https://github.com/mxr/sqlite-export-for-ynab
|
|
6
6
|
Author: Max R
|
|
@@ -272,7 +272,7 @@ WITH interest_by_account AS (
|
|
|
272
272
|
SELECT
|
|
273
273
|
plan_id
|
|
274
274
|
, account_name
|
|
275
|
-
, SUM(
|
|
275
|
+
, SUM(amount_currency) AS total
|
|
276
276
|
FROM flat_transactions
|
|
277
277
|
WHERE
|
|
278
278
|
TRUE
|
|
@@ -29,6 +29,7 @@ from sqlite_export_for_ynab._main import get_last_knowledge_of_server
|
|
|
29
29
|
from sqlite_export_for_ynab._main import get_relations
|
|
30
30
|
from sqlite_export_for_ynab._main import insert_accounts
|
|
31
31
|
from sqlite_export_for_ynab._main import insert_category_groups
|
|
32
|
+
from sqlite_export_for_ynab._main import insert_entries
|
|
32
33
|
from sqlite_export_for_ynab._main import insert_payees
|
|
33
34
|
from sqlite_export_for_ynab._main import insert_plans
|
|
34
35
|
from sqlite_export_for_ynab._main import insert_scheduled_transactions
|
|
@@ -499,6 +500,31 @@ async def test_insert_payees(context):
|
|
|
499
500
|
)
|
|
500
501
|
|
|
501
502
|
|
|
503
|
+
@pytest.mark.asyncio
|
|
504
|
+
async def test_insert_entries_ignores_unknown_keys(context):
|
|
505
|
+
task_id = context.progress.add_task("Payees", total=1)
|
|
506
|
+
entry = {
|
|
507
|
+
"id": PAYEE_ID_1,
|
|
508
|
+
"name": "Payee",
|
|
509
|
+
"transfer_account_id": None,
|
|
510
|
+
"deleted": False,
|
|
511
|
+
"brand_new_api_field": "surprise",
|
|
512
|
+
}
|
|
513
|
+
await insert_entries(context, "payees", PLAN_ID_1, [entry], task_id)
|
|
514
|
+
assert_rows(
|
|
515
|
+
await fetchall(context.con, "SELECT * FROM payees"),
|
|
516
|
+
[
|
|
517
|
+
{
|
|
518
|
+
"id": PAYEE_ID_1,
|
|
519
|
+
"plan_id": PLAN_ID_1,
|
|
520
|
+
"name": "Payee",
|
|
521
|
+
"transfer_account_id": None,
|
|
522
|
+
"deleted": False,
|
|
523
|
+
}
|
|
524
|
+
],
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
|
|
502
528
|
@pytest.mark.asyncio
|
|
503
529
|
async def test_insert_transactions(context):
|
|
504
530
|
await insert_transactions(context, PLAN_ID_1, [])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__init__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__main__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|