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.
Files changed (23) hide show
  1. {sqlite_export_for_ynab-2.8.0/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.9.0}/PKG-INFO +2 -2
  2. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/README.md +1 -1
  3. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/setup.cfg +1 -1
  4. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/_main.py +39 -86
  5. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0/sqlite_export_for_ynab.egg-info}/PKG-INFO +2 -2
  6. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/tests/_main_test.py +26 -0
  7. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/LICENSE +0 -0
  8. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/pyproject.toml +0 -0
  9. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/setup.py +0 -0
  10. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__init__.py +0 -0
  11. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/__main__.py +0 -0
  12. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
  13. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/create-relations.sql +0 -0
  14. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/ddl/drop-relations.sql +0 -0
  15. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab/py.typed +0 -0
  16. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
  17. {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
  18. {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
  19. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/sqlite_export_for_ynab.egg-info/requires.txt +0 -0
  20. {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
  21. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/testing/__init__.py +0 -0
  22. {sqlite_export_for_ynab-2.8.0 → sqlite_export_for_ynab-2.9.0}/testing/fixtures.py +0 -0
  23. {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.8.0
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(-amount_currency) AS total
275
+ , SUM(amount_currency) AS total
276
276
  FROM flat_transactions
277
277
  WHERE
278
278
  TRUE
@@ -242,7 +242,7 @@ WITH interest_by_account AS (
242
242
  SELECT
243
243
  plan_id
244
244
  , account_name
245
- , SUM(-amount_currency) AS total
245
+ , SUM(amount_currency) AS total
246
246
  FROM flat_transactions
247
247
  WHERE
248
248
  TRUE
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = sqlite_export_for_ynab
3
- version = 2.8.0
3
+ version = 2.9.0
4
4
  description = SQLite Export for YNAB - Export YNAB Data to SQLite
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -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
- entry_keys = tuple(entries[0])
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
- accounts,
639
- categories,
640
- payees,
641
- transactions_serverknowledge,
642
- scheduled_transactions,
643
- ) = await asyncio.gather(
644
- _get_accounts(context, plan_id, lkos, task_id),
645
- _get_categories(context, plan_id, lkos, task_id),
646
- _get_payees(context, plan_id, lkos, task_id),
647
- _get_transactions(context, plan_id, lkos, task_id),
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 _get_accounts(
666
- context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
667
- ) -> list[Account]:
668
- resp = await AccountsApi(context.api_client).get_accounts(
669
- plan_id=plan_id, last_knowledge_of_server=lkos.get(plan_id)
670
- )
671
- context.progress.update(task_id, advance=1)
672
- return resp.data.accounts
673
-
674
-
675
- @retry(stop=stop_after_attempt(3))
676
- async def _get_categories(
677
- context: _Context, plan_id: str, lkos: dict[str, int], task_id: TaskID
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.8.0
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(-amount_currency) AS total
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, [])