sqlite-export-for-ynab 2.9.5__tar.gz → 2.9.7__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 (22) hide show
  1. {sqlite_export_for_ynab-2.9.5/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.9.7}/PKG-INFO +35 -18
  2. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/README.md +32 -16
  3. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/pyproject.toml +11 -5
  4. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/_main.py +56 -46
  5. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7/sqlite_export_for_ynab.egg-info}/PKG-INFO +35 -18
  6. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab.egg-info/requires.txt +3 -0
  7. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/testing/fixtures.py +0 -1
  8. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/LICENSE +0 -0
  9. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/setup.cfg +0 -0
  10. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/__init__.py +0 -0
  11. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/__main__.py +0 -0
  12. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
  13. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/ddl/create-relations.sql +0 -0
  14. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/ddl/drop-relations.sql +0 -0
  15. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab/py.typed +0 -0
  16. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
  17. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab.egg-info/dependency_links.txt +0 -0
  18. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab.egg-info/entry_points.txt +0 -0
  19. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/sqlite_export_for_ynab.egg-info/top_level.txt +0 -0
  20. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/testing/__init__.py +0 -0
  21. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/tests/__init__.py +0 -0
  22. {sqlite_export_for_ynab-2.9.5 → sqlite_export_for_ynab-2.9.7}/tests/_main_test.py +9 -9
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlite_export_for_ynab
3
- Version: 2.9.5
3
+ Version: 2.9.7
4
4
  Summary: SQLite Export for YNAB - Export YNAB Data to SQLite
5
5
  Author-email: Max R <mxr@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -10,7 +10,7 @@ Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3 :: Only
11
11
  Classifier: Programming Language :: Python :: Implementation :: CPython
12
12
  Classifier: Programming Language :: Python :: Implementation :: PyPy
13
- Requires-Python: >=3.12
13
+ Requires-Python: >=3.11
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: aiohttp>=3
@@ -20,17 +20,20 @@ Requires-Dist: asyncio-for-ynab~=1.86.0
20
20
  Requires-Dist: fasteners
21
21
  Requires-Dist: rich>=10
22
22
  Requires-Dist: tenacity
23
+ Requires-Dist: typing-extensions; python_version < "3.12"
23
24
  Dynamic: license-file
24
25
 
25
26
  # sqlite-export-for-ynab
26
27
 
27
- [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main) [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
28
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
29
+ [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
28
30
 
29
31
  SQLite Export for YNAB - Export YNAB Budget Data to SQLite
30
32
 
31
33
  ## What This Does
32
34
 
33
- Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your data with any tools compatible with SQLite.
35
+ Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your
36
+ data with any tools compatible with SQLite.
34
37
 
35
38
  ## Installation
36
39
 
@@ -42,7 +45,8 @@ $ pip install sqlite-export-for-ynab
42
45
 
43
46
  ### CLI
44
47
 
45
- Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment variable.
48
+ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment
49
+ variable.
46
50
 
47
51
  ```console
48
52
  $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
@@ -54,17 +58,22 @@ Run the tool from the terminal to download your plans:
54
58
  $ sqlite-export-for-ynab
55
59
  ```
56
60
 
57
- Running it again will pull only data that changed since the last pull (this is done with [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the `--full-refresh` flag.
58
- Pass `--quiet` to suppress all CLI output, including progress bars.
61
+ Running it again will pull only data that changed since the last pull (this is done with
62
+ [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the
63
+ `--full-refresh` flag. Pass `--quiet` to suppress all CLI output, including progress bars.
59
64
 
60
65
  <a id="db-path"></a>You can specify the DB path with the following options
66
+
61
67
  1. The `--db` flag.
62
- 1. The `XDG_DATA_HOME` variable (see the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
63
- 1. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
68
+ 2. The `XDG_DATA_HOME` variable (see the
69
+ [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that
70
+ case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
71
+ 3. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
64
72
 
65
73
  ### Library
66
74
 
67
- The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use them as follows:
75
+ The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use
76
+ them as follows:
68
77
 
69
78
  ```python
70
79
  import asyncio
@@ -82,11 +91,17 @@ asyncio.run(sync(token, db, full_refresh))
82
91
 
83
92
  ## Relations
84
93
 
85
- The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some additions:
94
+ The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with
95
+ [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some
96
+ additions:
86
97
 
87
- 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions, loan account periodic values).
88
- 1. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
89
- 1. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions` respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields to make querying more ergonomic.
98
+ 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions,
99
+ loan account periodic values).
100
+ 2. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
101
+ 3. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and
102
+ non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions`
103
+ respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields
104
+ to make querying more ergonomic.
90
105
 
91
106
  ## Querying
92
107
 
@@ -96,10 +111,10 @@ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` delib
96
111
 
97
112
  You can run the queries from this README using a tool like [`mdq`](https://github.com/yshavit/mdq). For example:
98
113
 
99
- ```console
114
+ ````console
100
115
  $ mdq '```sql dupes' path/to/sqlite-export-for-ynab/README.md -o plain \
101
116
  | sqlite3 path/to/sqlite-export-for-ynab/db.sqlite
102
- ```
117
+ ````
103
118
 
104
119
  The DB path is documented [above](#db-path).
105
120
 
@@ -233,7 +248,7 @@ FROM (
233
248
  ;
234
249
  ```
235
250
 
236
- To estimate taxable interest for a given year[^1]:
251
+ To estimate taxable interest for a given year\[^1\]:
237
252
 
238
253
  ```sql
239
254
  -- Parameters expected by this query:
@@ -591,4 +606,6 @@ WHERE
591
606
  ;
592
607
  ```
593
608
 
594
- [^1]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified professional.
609
+ \[^1\]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax
610
+ advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified
611
+ professional.
@@ -1,12 +1,14 @@
1
1
  # sqlite-export-for-ynab
2
2
 
3
- [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main) [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
3
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
4
+ [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
4
5
 
5
6
  SQLite Export for YNAB - Export YNAB Budget Data to SQLite
6
7
 
7
8
  ## What This Does
8
9
 
9
- Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your data with any tools compatible with SQLite.
10
+ Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your
11
+ data with any tools compatible with SQLite.
10
12
 
11
13
  ## Installation
12
14
 
@@ -18,7 +20,8 @@ $ pip install sqlite-export-for-ynab
18
20
 
19
21
  ### CLI
20
22
 
21
- Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment variable.
23
+ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment
24
+ variable.
22
25
 
23
26
  ```console
24
27
  $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
@@ -30,17 +33,22 @@ Run the tool from the terminal to download your plans:
30
33
  $ sqlite-export-for-ynab
31
34
  ```
32
35
 
33
- Running it again will pull only data that changed since the last pull (this is done with [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the `--full-refresh` flag.
34
- Pass `--quiet` to suppress all CLI output, including progress bars.
36
+ Running it again will pull only data that changed since the last pull (this is done with
37
+ [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the
38
+ `--full-refresh` flag. Pass `--quiet` to suppress all CLI output, including progress bars.
35
39
 
36
40
  <a id="db-path"></a>You can specify the DB path with the following options
41
+
37
42
  1. The `--db` flag.
38
- 1. The `XDG_DATA_HOME` variable (see the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
39
- 1. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
43
+ 2. The `XDG_DATA_HOME` variable (see the
44
+ [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that
45
+ case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
46
+ 3. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
40
47
 
41
48
  ### Library
42
49
 
43
- The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use them as follows:
50
+ The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use
51
+ them as follows:
44
52
 
45
53
  ```python
46
54
  import asyncio
@@ -58,11 +66,17 @@ asyncio.run(sync(token, db, full_refresh))
58
66
 
59
67
  ## Relations
60
68
 
61
- The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some additions:
69
+ The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with
70
+ [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some
71
+ additions:
62
72
 
63
- 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions, loan account periodic values).
64
- 1. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
65
- 1. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions` respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields to make querying more ergonomic.
73
+ 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions,
74
+ loan account periodic values).
75
+ 2. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
76
+ 3. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and
77
+ non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions`
78
+ respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields
79
+ to make querying more ergonomic.
66
80
 
67
81
  ## Querying
68
82
 
@@ -72,10 +86,10 @@ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` delib
72
86
 
73
87
  You can run the queries from this README using a tool like [`mdq`](https://github.com/yshavit/mdq). For example:
74
88
 
75
- ```console
89
+ ````console
76
90
  $ mdq '```sql dupes' path/to/sqlite-export-for-ynab/README.md -o plain \
77
91
  | sqlite3 path/to/sqlite-export-for-ynab/db.sqlite
78
- ```
92
+ ````
79
93
 
80
94
  The DB path is documented [above](#db-path).
81
95
 
@@ -209,7 +223,7 @@ FROM (
209
223
  ;
210
224
  ```
211
225
 
212
- To estimate taxable interest for a given year[^1]:
226
+ To estimate taxable interest for a given year\[^1\]:
213
227
 
214
228
  ```sql
215
229
  -- Parameters expected by this query:
@@ -567,4 +581,6 @@ WHERE
567
581
  ;
568
582
  ```
569
583
 
570
- [^1]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified professional.
584
+ \[^1\]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax
585
+ advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified
586
+ professional.
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "fasteners",
27
27
  "rich>=10",
28
28
  "tenacity",
29
+ "typing-extensions; python_version<'3.12'",
29
30
  ]
30
31
  description = "SQLite Export for YNAB - Export YNAB Data to SQLite"
31
32
  keywords = ["ynab", "sqlite", "sql", "budget", "plan", "cli"]
@@ -33,8 +34,8 @@ license = "MIT"
33
34
  license-files = ["LICENSE"]
34
35
  name = "sqlite_export_for_ynab"
35
36
  readme = "README.md"
36
- requires-python = ">=3.12"
37
- version = "2.9.5"
37
+ requires-python = ">=3.11"
38
+ version = "2.9.7"
38
39
 
39
40
  [project.scripts]
40
41
  sqlite-export-for-ynab = "sqlite_export_for_ynab._main:main"
@@ -65,18 +66,23 @@ module = "tests.*"
65
66
  asyncio_default_fixture_loop_scope = "function"
66
67
 
67
68
  [tool.ruff]
68
- target-version = "py312"
69
+ target-version = "py311"
69
70
 
70
71
  [tool.ruff.lint]
71
72
  extend-select = [
72
- "UP", # see pyupgrade
73
- "B", # see flake8-bugbear
74
73
  "A", # see flake8-builtins
74
+ "B", # see flake8-bugbear
75
75
  "C4", # see flake8-comprehension
76
+ "I", # sort imports
76
77
  "SIM", # see flake8-simplify
77
78
  "TC", # see flake8-type-checking
79
+ "UP", # see pyupgrade
78
80
  ]
79
81
 
82
+ [tool.ruff.lint.isort]
83
+ force-single-line = true
84
+ required-imports = ["from __future__ import annotations"]
85
+
80
86
  [tool.setuptools.package-data]
81
87
  sqlite_export_for_ynab = ["py.typed"]
82
88
  "sqlite_export_for_ynab.ddl" = ["*.sql"]
@@ -3,21 +3,24 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import asyncio
5
5
  import os
6
+ import sys
6
7
  from contextlib import asynccontextmanager
7
8
  from contextlib import contextmanager
8
9
  from dataclasses import dataclass
9
10
  from dataclasses import fields
10
11
  from datetime import date
12
+ from datetime import datetime
11
13
  from datetime import timedelta
12
14
  from importlib import resources
13
15
  from importlib.metadata import version
14
- from itertools import batched
15
16
  from pathlib import Path
17
+ from typing import TYPE_CHECKING
16
18
  from typing import Any
17
19
  from typing import Literal
20
+ from typing import TypeVar
21
+ from typing import cast
22
+ from typing import get_args
18
23
  from typing import overload
19
- from typing import override
20
- from typing import TYPE_CHECKING
21
24
 
22
25
  import aiosqlite
23
26
  import asyncio_for_ynab # noqa: F401
@@ -53,11 +56,30 @@ if TYPE_CHECKING:
53
56
  from collections.abc import AsyncIterator
54
57
  from collections.abc import Awaitable
55
58
  from collections.abc import Callable
59
+ from collections.abc import Iterable
56
60
  from collections.abc import Iterator
57
61
  from collections.abc import Sequence
58
62
 
63
+ _T = TypeVar("_T")
64
+
65
+ if sys.version_info >= (3, 12): # pragma: >=3.12 cover
66
+ from itertools import batched
67
+ from typing import override
68
+ else: # pragma: <3.12 cover
69
+ from itertools import islice
70
+
71
+ from typing_extensions import override
72
+
73
+ def batched(iterable: Iterable[_T], n: int) -> Iterator[tuple[_T, ...]]:
74
+ it = iter(iterable)
75
+ while batch := tuple(islice(it, n)):
76
+ yield batch
77
+
78
+
59
79
  try:
60
- from rich.progress import MofNCompleteColumn
80
+ from rich.progress import (
81
+ MofNCompleteColumn, # pyright: ignore[reportAssignmentType]
82
+ )
61
83
  # https://github.com/benleb/surepy/issues/240
62
84
  except ImportError: # pragma: no cover
63
85
  from rich.progress import ProgressColumn
@@ -84,28 +106,24 @@ except ImportError: # pragma: no cover
84
106
  )
85
107
 
86
108
 
87
- _EntryTable = (
88
- Literal["accounts"]
89
- | Literal["account_periodic_values"]
90
- | Literal["category_groups"]
91
- | Literal["categories"]
92
- | Literal["payees"]
93
- | Literal["transactions"]
94
- | Literal["subtransactions"]
95
- | Literal["scheduled_transactions"]
96
- | Literal["scheduled_subtransactions"]
97
- )
98
- _Endpoint = (
99
- Literal["accounts"]
100
- | Literal["categories"]
101
- | Literal["payees"]
102
- | Literal["transactions"]
103
- | Literal["scheduled_transactions"]
104
- )
105
- _ENDPOINTS = tuple(lit.__args__[0] for lit in _Endpoint.__args__)
109
+ _EntryTable = Literal[
110
+ "accounts",
111
+ "account_periodic_values",
112
+ "category_groups",
113
+ "categories",
114
+ "payees",
115
+ "transactions",
116
+ "subtransactions",
117
+ "scheduled_transactions",
118
+ "scheduled_subtransactions",
119
+ ]
120
+ _Endpoint = Literal[
121
+ "accounts", "categories", "payees", "transactions", "scheduled_transactions"
122
+ ]
123
+ _ENDPOINTS = get_args(_Endpoint)
106
124
  _ALL_RELATIONS = frozenset(
107
125
  ("plans", "flat_transactions", "scheduled_flat_transactions")
108
- + tuple(lit.__args__[0] for lit in _EntryTable.__args__)
126
+ + get_args(_EntryTable)
109
127
  )
110
128
 
111
129
  _ENV_TOKEN = "YNAB_PERSONAL_ACCESS_TOKEN"
@@ -159,9 +177,9 @@ async def async_main(
159
177
  )
160
178
 
161
179
  args = parser.parse_args(argv)
162
- db: Path = args.db
163
- full_refresh: bool = args.full_refresh
164
- quiet: bool = args.quiet
180
+ db = cast("Path", args.db)
181
+ full_refresh = cast("bool", args.full_refresh)
182
+ quiet = cast("bool", args.quiet)
165
183
 
166
184
  token = resolve_token(token_override)
167
185
 
@@ -556,28 +574,20 @@ async def insert_nested_entries(
556
574
  context: _Context,
557
575
  plan_id: str,
558
576
  entries: list[dict[str, Any]],
559
- desc: (
560
- Literal["Accounts"]
561
- | Literal["Categories"]
562
- | Literal["Transactions"]
563
- | Literal["Scheduled Transactions"]
564
- ),
577
+ desc: (Literal["Accounts", "Categories", "Transactions", "Scheduled Transactions"]),
565
578
  entries_name: (
566
- Literal["accounts"]
567
- | Literal["category_groups"]
568
- | Literal["transactions"]
569
- | Literal["scheduled_transactions"]
579
+ Literal["accounts", "category_groups", "transactions", "scheduled_transactions"]
570
580
  ),
571
581
  subentries_name: (
572
- Literal["account_periodic_values"]
573
- | Literal["categories"]
574
- | Literal["subtransactions"]
582
+ Literal["account_periodic_values", "categories", "subtransactions"]
575
583
  ),
576
584
  subentries_table_name: (
577
- Literal["account_periodic_values"]
578
- | Literal["categories"]
579
- | Literal["subtransactions"]
580
- | Literal["scheduled_subtransactions"]
585
+ Literal[
586
+ "account_periodic_values",
587
+ "categories",
588
+ "subtransactions",
589
+ "scheduled_subtransactions",
590
+ ]
581
591
  ),
582
592
  ) -> None:
583
593
  if not entries:
@@ -684,7 +694,7 @@ class _ProgressYnab:
684
694
  task_id: TaskID
685
695
 
686
696
  @retry(stop=stop_after_attempt(3))
687
- async def get[T](self, endpoint: Callable[..., Awaitable[T]]) -> T:
697
+ async def get(self, endpoint: Callable[..., Awaitable[_T]]) -> _T:
688
698
  try:
689
699
  return await endpoint(
690
700
  plan_id=self.plan_id,
@@ -713,7 +723,7 @@ class ChunkedTransactionsApi:
713
723
  async def _get(
714
724
  self, transactions_api: TransactionsApi, plan_id: str
715
725
  ) -> TransactionsResponse:
716
- today = date.today()
726
+ today = datetime.now().astimezone().date()
717
727
  responses = await asyncio.gather(
718
728
  *(
719
729
  self._chunk(transactions_api, plan_id, sd, ud)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlite_export_for_ynab
3
- Version: 2.9.5
3
+ Version: 2.9.7
4
4
  Summary: SQLite Export for YNAB - Export YNAB Data to SQLite
5
5
  Author-email: Max R <mxr@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -10,7 +10,7 @@ Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3 :: Only
11
11
  Classifier: Programming Language :: Python :: Implementation :: CPython
12
12
  Classifier: Programming Language :: Python :: Implementation :: PyPy
13
- Requires-Python: >=3.12
13
+ Requires-Python: >=3.11
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: aiohttp>=3
@@ -20,17 +20,20 @@ Requires-Dist: asyncio-for-ynab~=1.86.0
20
20
  Requires-Dist: fasteners
21
21
  Requires-Dist: rich>=10
22
22
  Requires-Dist: tenacity
23
+ Requires-Dist: typing-extensions; python_version < "3.12"
23
24
  Dynamic: license-file
24
25
 
25
26
  # sqlite-export-for-ynab
26
27
 
27
- [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main) [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
28
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mxr/sqlite-export-for-ynab/main.svg)](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
29
+ [![codecov](https://codecov.io/github/mxr/sqlite-export-for-ynab/graph/badge.svg?token=NVCP6RDKSH)](https://codecov.io/github/mxr/sqlite-export-for-ynab)
28
30
 
29
31
  SQLite Export for YNAB - Export YNAB Budget Data to SQLite
30
32
 
31
33
  ## What This Does
32
34
 
33
- Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your data with any tools compatible with SQLite.
35
+ Export all your [YNAB](https://ynab.com/) plans to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your
36
+ data with any tools compatible with SQLite.
34
37
 
35
38
  ## Installation
36
39
 
@@ -42,7 +45,8 @@ $ pip install sqlite-export-for-ynab
42
45
 
43
46
  ### CLI
44
47
 
45
- Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment variable.
48
+ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment
49
+ variable.
46
50
 
47
51
  ```console
48
52
  $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
@@ -54,17 +58,22 @@ Run the tool from the terminal to download your plans:
54
58
  $ sqlite-export-for-ynab
55
59
  ```
56
60
 
57
- Running it again will pull only data that changed since the last pull (this is done with [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the `--full-refresh` flag.
58
- Pass `--quiet` to suppress all CLI output, including progress bars.
61
+ Running it again will pull only data that changed since the last pull (this is done with
62
+ [Delta Requests](https://api.ynab.com/#deltas)). If you want to wipe the DB and pull all data again use the
63
+ `--full-refresh` flag. Pass `--quiet` to suppress all CLI output, including progress bars.
59
64
 
60
65
  <a id="db-path"></a>You can specify the DB path with the following options
66
+
61
67
  1. The `--db` flag.
62
- 1. The `XDG_DATA_HOME` variable (see the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
63
- 1. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
68
+ 2. The `XDG_DATA_HOME` variable (see the
69
+ [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/index.html)). In that
70
+ case the DB is saved in `"${XDG_DATA_HOME}"/sqlite-export-for-ynab/db.sqlite`.
71
+ 3. If neither is set, the DB is saved in `~/.local/share/sqlite-export-for-ynab/db.sqlite`.
64
72
 
65
73
  ### Library
66
74
 
67
- The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use them as follows:
75
+ The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use
76
+ them as follows:
68
77
 
69
78
  ```python
70
79
  import asyncio
@@ -82,11 +91,17 @@ asyncio.run(sync(token, db, full_refresh))
82
91
 
83
92
  ## Relations
84
93
 
85
- The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some additions:
94
+ The relations are defined in [create-relations.sql](sqlite_export_for_ynab/ddl/create-relations.sql). They are 1:1 with
95
+ [YNAB's OpenAPI Spec](https://api.ynab.com/papi/open_api_spec.yaml) (ex: transactions, accounts, etc) with some
96
+ additions:
86
97
 
87
- 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions, loan account periodic values).
88
- 1. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
89
- 1. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions` respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields to make querying more ergonomic.
98
+ 1. Some objects are pulled out into their own tables so they can be more cleanly modeled in SQLite (ex: subtransactions,
99
+ loan account periodic values).
100
+ 2. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
101
+ 3. Two new views called `flat_transactions` and `scheduled_flat_transactions`. These allow you to query split and
102
+ non-split transactions easily, without needing to also query `subtransactions` and `scheduled_subtransactions`
103
+ respectively. They also filter out deleted/unapproved transactions/subtransactions and project payee/category fields
104
+ to make querying more ergonomic.
90
105
 
91
106
  ## Querying
92
107
 
@@ -96,10 +111,10 @@ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` delib
96
111
 
97
112
  You can run the queries from this README using a tool like [`mdq`](https://github.com/yshavit/mdq). For example:
98
113
 
99
- ```console
114
+ ````console
100
115
  $ mdq '```sql dupes' path/to/sqlite-export-for-ynab/README.md -o plain \
101
116
  | sqlite3 path/to/sqlite-export-for-ynab/db.sqlite
102
- ```
117
+ ````
103
118
 
104
119
  The DB path is documented [above](#db-path).
105
120
 
@@ -233,7 +248,7 @@ FROM (
233
248
  ;
234
249
  ```
235
250
 
236
- To estimate taxable interest for a given year[^1]:
251
+ To estimate taxable interest for a given year\[^1\]:
237
252
 
238
253
  ```sql
239
254
  -- Parameters expected by this query:
@@ -591,4 +606,6 @@ WHERE
591
606
  ;
592
607
  ```
593
608
 
594
- [^1]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified professional.
609
+ \[^1\]: This query is a rough estimate based on YNAB data and optional user inputs. It is not financial advice, tax
610
+ advice, or a substitute for Forms 1099-INT, brokerage statements, bank records, or guidance from a qualified
611
+ professional.
@@ -5,3 +5,6 @@ asyncio-for-ynab~=1.86.0
5
5
  fasteners
6
6
  rich>=10
7
7
  tenacity
8
+
9
+ [:python_version < "3.12"]
10
+ typing-extensions
@@ -29,7 +29,6 @@ from asyncio_for_ynab import TransactionDetail
29
29
  from asyncio_for_ynab import TransactionsResponse
30
30
  from asyncio_for_ynab import TransactionsResponseData
31
31
 
32
-
33
32
  PLAN_ID_1 = str(uuid4())
34
33
  PLAN_ID_2 = str(uuid4())
35
34
 
@@ -15,16 +15,16 @@ from rich.progress import Progress
15
15
 
16
16
  from sqlite_export_for_ynab import default_db_path
17
17
  from sqlite_export_for_ynab._main import _ALL_RELATIONS
18
- from sqlite_export_for_ynab._main import _Context
19
- from sqlite_export_for_ynab._main import _context
20
18
  from sqlite_export_for_ynab._main import _ENV_TOKEN
21
- from sqlite_export_for_ynab._main import _get_plan_summaries
22
19
  from sqlite_export_for_ynab._main import _PACKAGE
23
20
  from sqlite_export_for_ynab._main import _PROGRESS_COLUMNS
21
+ from sqlite_export_for_ynab._main import ChunkedTransactionsApi
22
+ from sqlite_export_for_ynab._main import _Context
23
+ from sqlite_export_for_ynab._main import _context
24
+ from sqlite_export_for_ynab._main import _get_plan_summaries
24
25
  from sqlite_export_for_ynab._main import _quarterly
25
26
  from sqlite_export_for_ynab._main import async_main
26
27
  from sqlite_export_for_ynab._main import asyncio_for_ynab
27
- from sqlite_export_for_ynab._main import ChunkedTransactionsApi
28
28
  from sqlite_export_for_ynab._main import contents
29
29
  from sqlite_export_for_ynab._main import get_last_knowledge_of_server
30
30
  from sqlite_export_for_ynab._main import get_relations
@@ -41,8 +41,6 @@ from sqlite_export_for_ynab._main import sync
41
41
  from testing.fixtures import ACCOUNT_ID_1
42
42
  from testing.fixtures import ACCOUNT_ID_2
43
43
  from testing.fixtures import ACCOUNTS
44
- from testing.fixtures import accounts_response
45
- from testing.fixtures import categories_response
46
44
  from testing.fixtures import CATEGORY_GOAL_TARGET_DATE_1
47
45
  from testing.fixtures import CATEGORY_GROUP_ID_1
48
46
  from testing.fixtures import CATEGORY_GROUP_ID_2
@@ -61,10 +59,8 @@ from testing.fixtures import LKOS
61
59
  from testing.fixtures import PAYEE_ID_1
62
60
  from testing.fixtures import PAYEE_ID_2
63
61
  from testing.fixtures import PAYEES
64
- from testing.fixtures import payees_response
65
62
  from testing.fixtures import PLAN_ID_1
66
63
  from testing.fixtures import PLAN_ID_2
67
- from testing.fixtures import plan_response
68
64
  from testing.fixtures import PLANS
69
65
  from testing.fixtures import SCHEDULED_SUBTRANSACTION_ID_1
70
66
  from testing.fixtures import SCHEDULED_SUBTRANSACTION_ID_2
@@ -72,7 +68,6 @@ from testing.fixtures import SCHEDULED_TRANSACTION_ID_1
72
68
  from testing.fixtures import SCHEDULED_TRANSACTION_ID_2
73
69
  from testing.fixtures import SCHEDULED_TRANSACTION_ID_3
74
70
  from testing.fixtures import SCHEDULED_TRANSACTIONS
75
- from testing.fixtures import scheduled_transactions_response
76
71
  from testing.fixtures import SERVER_KNOWLEDGE_1
77
72
  from testing.fixtures import SERVER_KNOWLEDGE_2
78
73
  from testing.fixtures import SUBTRANSACTION_ID_1
@@ -83,6 +78,11 @@ from testing.fixtures import TRANSACTION_ID_1
83
78
  from testing.fixtures import TRANSACTION_ID_2
84
79
  from testing.fixtures import TRANSACTION_ID_3
85
80
  from testing.fixtures import TRANSACTIONS
81
+ from testing.fixtures import accounts_response
82
+ from testing.fixtures import categories_response
83
+ from testing.fixtures import payees_response
84
+ from testing.fixtures import plan_response
85
+ from testing.fixtures import scheduled_transactions_response
86
86
  from testing.fixtures import transactions_response
87
87
 
88
88