sqlite-export-for-ynab 2.9.6__tar.gz → 2.9.8__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.9.6 → sqlite_export_for_ynab-2.9.8}/LICENSE +0 -2
- {sqlite_export_for_ynab-2.9.6/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.9.8}/PKG-INFO +37 -20
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/README.md +32 -16
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/pyproject.toml +51 -53
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/_main.py +27 -7
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8/sqlite_export_for_ynab.egg-info}/PKG-INFO +37 -20
- sqlite_export_for_ynab-2.9.8/sqlite_export_for_ynab.egg-info/requires.txt +10 -0
- sqlite_export_for_ynab-2.9.6/sqlite_export_for_ynab.egg-info/requires.txt +0 -7
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/setup.cfg +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/__main__.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/ddl/create-relations.sql +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/ddl/drop-relations.sql +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/py.typed +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab.egg-info/dependency_links.txt +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab.egg-info/entry_points.txt +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab.egg-info/top_level.txt +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/testing/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/testing/fixtures.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/tests/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/tests/_main_test.py +0 -0
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.8
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://github.com/mxr/sqlite-export-for-ynab
|
|
8
|
-
Keywords:
|
|
8
|
+
Keywords: budget,cli,plan,sql,sqlite,ynab
|
|
9
9
|
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.
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
16
|
Requires-Dist: aiohttp>=3
|
|
17
17
|
Requires-Dist: aiopathlib
|
|
18
18
|
Requires-Dist: aiosqlite
|
|
19
|
-
Requires-Dist: asyncio-for-ynab~=1.
|
|
19
|
+
Requires-Dist: asyncio-for-ynab~=1.87.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
|
-
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
28
|
+
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
29
|
+
[](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
|
|
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
|
|
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
|
|
58
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
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
|
|
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,
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
3
|
+
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
4
|
+
[](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
|
|
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
|
|
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
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
|
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
|
|
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,
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
build-backend = "setuptools.build_meta"
|
|
3
|
-
requires = ["setuptools"]
|
|
4
|
-
|
|
5
|
-
[dependency-groups]
|
|
6
|
-
dev = [
|
|
7
|
-
"covdefaults>=2.1.0",
|
|
8
|
-
"coverage",
|
|
9
|
-
"pytest",
|
|
10
|
-
"pytest-asyncio",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
1
|
[project]
|
|
14
|
-
|
|
2
|
+
name = "sqlite_export_for_ynab"
|
|
3
|
+
version = "2.9.8"
|
|
4
|
+
description = "SQLite Export for YNAB - Export YNAB Data to SQLite"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11" # generated by mxr/base
|
|
7
|
+
license = "MIT" # generated by mxr/base
|
|
8
|
+
license-files = ["LICENSE"] # generated by mxr/base
|
|
9
|
+
authors = [{ name = "Max R", email = "mxr@users.noreply.github.com" }] # generated by mxr/base
|
|
10
|
+
keywords = ["budget", "cli", "plan", "sql", "sqlite", "ynab"]
|
|
15
11
|
classifiers = [
|
|
16
12
|
"Programming Language :: Python :: 3",
|
|
17
13
|
"Programming Language :: Python :: 3 :: Only",
|
|
@@ -22,30 +18,35 @@ dependencies = [
|
|
|
22
18
|
"aiohttp>=3",
|
|
23
19
|
"aiopathlib",
|
|
24
20
|
"aiosqlite",
|
|
25
|
-
"asyncio-for-ynab~=1.
|
|
21
|
+
"asyncio-for-ynab~=1.87.0",
|
|
26
22
|
"fasteners",
|
|
27
23
|
"rich>=10",
|
|
28
24
|
"tenacity",
|
|
25
|
+
"typing-extensions; python_version<'3.12'",
|
|
29
26
|
]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
license-files = ["LICENSE"]
|
|
34
|
-
name = "sqlite_export_for_ynab"
|
|
35
|
-
readme = "README.md"
|
|
36
|
-
requires-python = ">=3.12"
|
|
37
|
-
version = "2.9.6"
|
|
27
|
+
|
|
28
|
+
[project.urls] # generated by mxr/base
|
|
29
|
+
Homepage = "https://github.com/mxr/sqlite-export-for-ynab"
|
|
38
30
|
|
|
39
31
|
[project.scripts]
|
|
40
32
|
sqlite-export-for-ynab = "sqlite_export_for_ynab._main:main"
|
|
41
33
|
|
|
42
|
-
[
|
|
43
|
-
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
dev = [
|
|
36
|
+
"covdefaults>=2.1.0",
|
|
37
|
+
"coverage",
|
|
38
|
+
"pytest",
|
|
39
|
+
"pytest-asyncio",
|
|
40
|
+
]
|
|
44
41
|
|
|
45
|
-
[
|
|
42
|
+
[build-system] # generated by mxr/base
|
|
43
|
+
requires = ["setuptools"]
|
|
44
|
+
build-backend = "setuptools.build_meta"
|
|
45
|
+
|
|
46
|
+
[tool.coverage.run] # generated by mxr/base
|
|
46
47
|
plugins = ["covdefaults"]
|
|
47
48
|
|
|
48
|
-
[tool.mypy]
|
|
49
|
+
[tool.mypy] # generated by mxr/base
|
|
49
50
|
check_untyped_defs = true
|
|
50
51
|
disallow_any_generics = true
|
|
51
52
|
disallow_incomplete_defs = true
|
|
@@ -53,21 +54,18 @@ disallow_untyped_defs = true
|
|
|
53
54
|
warn_redundant_casts = true
|
|
54
55
|
warn_unused_ignores = true
|
|
55
56
|
|
|
56
|
-
[[tool.mypy.overrides]]
|
|
57
|
+
[[tool.mypy.overrides]] # generated by mxr/base
|
|
57
58
|
disallow_untyped_defs = false
|
|
58
59
|
module = "testing.*"
|
|
59
60
|
|
|
60
|
-
[[tool.mypy.overrides]]
|
|
61
|
+
[[tool.mypy.overrides]] # generated by mxr/base
|
|
61
62
|
disallow_untyped_defs = false
|
|
62
63
|
module = "tests.*"
|
|
63
64
|
|
|
64
65
|
[tool.pytest.ini_options]
|
|
65
66
|
asyncio_default_fixture_loop_scope = "function"
|
|
66
67
|
|
|
67
|
-
[tool.ruff]
|
|
68
|
-
target-version = "py312"
|
|
69
|
-
|
|
70
|
-
[tool.ruff.lint]
|
|
68
|
+
[tool.ruff.lint] # generated by mxr/base
|
|
71
69
|
extend-select = [
|
|
72
70
|
"A", # see flake8-builtins
|
|
73
71
|
"B", # see flake8-bugbear
|
|
@@ -78,7 +76,7 @@ extend-select = [
|
|
|
78
76
|
"UP", # see pyupgrade
|
|
79
77
|
]
|
|
80
78
|
|
|
81
|
-
[tool.ruff.lint.isort]
|
|
79
|
+
[tool.ruff.lint.isort] # generated by mxr/base
|
|
82
80
|
force-single-line = true
|
|
83
81
|
required-imports = ["from __future__ import annotations"]
|
|
84
82
|
|
|
@@ -86,50 +84,50 @@ required-imports = ["from __future__ import annotations"]
|
|
|
86
84
|
sqlite_export_for_ynab = ["py.typed"]
|
|
87
85
|
"sqlite_export_for_ynab.ddl" = ["*.sql"]
|
|
88
86
|
|
|
89
|
-
[tool.setuptools.packages]
|
|
87
|
+
[tool.setuptools.packages] # generated by mxr/base
|
|
90
88
|
find = {}
|
|
91
89
|
|
|
92
|
-
[tool.sqlfluff.layout.type.comma]
|
|
90
|
+
[tool.sqlfluff.layout.type.comma] # generated by mxr/base
|
|
93
91
|
line_position = "leading"
|
|
94
92
|
spacing_before = "touch"
|
|
95
93
|
|
|
96
|
-
[tool.sqlfluff.layout.type.from_clause]
|
|
94
|
+
[tool.sqlfluff.layout.type.from_clause] # generated by mxr/base
|
|
97
95
|
keyword_line_position = "leading"
|
|
98
96
|
|
|
99
|
-
[tool.sqlfluff.layout.type.when_clause]
|
|
97
|
+
[tool.sqlfluff.layout.type.when_clause] # generated by mxr/base
|
|
100
98
|
keyword_line_position = "leading"
|
|
101
99
|
|
|
102
|
-
[tool.sqlfluff.rules.capitalisation.functions]
|
|
103
|
-
extended_capitalisation_policy =
|
|
100
|
+
[tool.sqlfluff.rules.capitalisation.functions] # generated by mxr/base
|
|
101
|
+
extended_capitalisation_policy = "upper"
|
|
104
102
|
|
|
105
|
-
[tool.sqlfluff.rules.capitalisation.identifiers]
|
|
106
|
-
extended_capitalisation_policy =
|
|
103
|
+
[tool.sqlfluff.rules.capitalisation.identifiers] # generated by mxr/base
|
|
104
|
+
extended_capitalisation_policy = "lower"
|
|
107
105
|
|
|
108
|
-
[tool.sqlfluff.rules.capitalisation.keywords]
|
|
109
|
-
capitalisation_policy =
|
|
106
|
+
[tool.sqlfluff.rules.capitalisation.keywords] # generated by mxr/base
|
|
107
|
+
capitalisation_policy = "upper"
|
|
110
108
|
|
|
111
|
-
[tool.sqlfluff.rules.capitalisation.literals]
|
|
112
|
-
capitalisation_policy =
|
|
109
|
+
[tool.sqlfluff.rules.capitalisation.literals] # generated by mxr/base
|
|
110
|
+
capitalisation_policy = "upper"
|
|
113
111
|
|
|
114
|
-
[tool.sqlfluff.rules.convention.not_equal]
|
|
112
|
+
[tool.sqlfluff.rules.convention.not_equal] # generated by mxr/base
|
|
115
113
|
preferred_not_equal_style = "c_style"
|
|
116
114
|
|
|
117
|
-
[tool.sqlfluff.rules.convention.terminator]
|
|
115
|
+
[tool.sqlfluff.rules.convention.terminator] # generated by mxr/base
|
|
118
116
|
multiline_newline = true
|
|
119
117
|
require_final_semicolon = true
|
|
120
118
|
|
|
121
|
-
[tool.sqlfluff.rules.references.quoting]
|
|
119
|
+
[tool.sqlfluff.rules.references.quoting] # generated by mxr/base
|
|
122
120
|
prefer_quoted_keywords = true
|
|
123
121
|
|
|
124
|
-
[tool.tox]
|
|
125
|
-
env_list = ["py", "
|
|
122
|
+
[tool.tox] # generated by mxr/base
|
|
123
|
+
env_list = ["py", "pre-commit"]
|
|
126
124
|
|
|
127
|
-
[tool.tox.env.pre-commit]
|
|
125
|
+
[tool.tox.env.pre-commit] # generated by mxr/base
|
|
128
126
|
commands = [["pre-commit", "run", "--all-files", "--show-diff-on-failure"]]
|
|
129
127
|
deps = ["pre-commit-uv"]
|
|
130
128
|
skip_install = true
|
|
131
129
|
|
|
132
|
-
[tool.tox.env_run_base]
|
|
130
|
+
[tool.tox.env_run_base] # generated by mxr/base
|
|
133
131
|
commands = [
|
|
134
132
|
["coverage", "erase"],
|
|
135
133
|
["coverage", "run", "-m", "pytest", "{posargs:tests}"],
|
|
@@ -137,5 +135,5 @@ commands = [
|
|
|
137
135
|
]
|
|
138
136
|
dependency_groups = ["dev"]
|
|
139
137
|
|
|
140
|
-
[tool.ty.rules]
|
|
138
|
+
[tool.ty.rules] # generated by mxr/base
|
|
141
139
|
all = "error"
|
{sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/_main.py
RENAMED
|
@@ -3,6 +3,7 @@ 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
|
|
@@ -12,14 +13,14 @@ from datetime import datetime
|
|
|
12
13
|
from datetime import timedelta
|
|
13
14
|
from importlib import resources
|
|
14
15
|
from importlib.metadata import version
|
|
15
|
-
from itertools import batched
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import TYPE_CHECKING
|
|
18
18
|
from typing import Any
|
|
19
19
|
from typing import Literal
|
|
20
|
+
from typing import TypeVar
|
|
21
|
+
from typing import cast
|
|
20
22
|
from typing import get_args
|
|
21
23
|
from typing import overload
|
|
22
|
-
from typing import override
|
|
23
24
|
|
|
24
25
|
import aiosqlite
|
|
25
26
|
import asyncio_for_ynab # noqa: F401
|
|
@@ -55,11 +56,30 @@ if TYPE_CHECKING:
|
|
|
55
56
|
from collections.abc import AsyncIterator
|
|
56
57
|
from collections.abc import Awaitable
|
|
57
58
|
from collections.abc import Callable
|
|
59
|
+
from collections.abc import Iterable
|
|
58
60
|
from collections.abc import Iterator
|
|
59
61
|
from collections.abc import Sequence
|
|
60
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
|
+
|
|
61
79
|
try:
|
|
62
|
-
from rich.progress import
|
|
80
|
+
from rich.progress import (
|
|
81
|
+
MofNCompleteColumn, # pyright: ignore[reportAssignmentType]
|
|
82
|
+
)
|
|
63
83
|
# https://github.com/benleb/surepy/issues/240
|
|
64
84
|
except ImportError: # pragma: no cover
|
|
65
85
|
from rich.progress import ProgressColumn
|
|
@@ -157,9 +177,9 @@ async def async_main(
|
|
|
157
177
|
)
|
|
158
178
|
|
|
159
179
|
args = parser.parse_args(argv)
|
|
160
|
-
db
|
|
161
|
-
full_refresh
|
|
162
|
-
quiet
|
|
180
|
+
db = cast("Path", args.db)
|
|
181
|
+
full_refresh = cast("bool", args.full_refresh)
|
|
182
|
+
quiet = cast("bool", args.quiet)
|
|
163
183
|
|
|
164
184
|
token = resolve_token(token_override)
|
|
165
185
|
|
|
@@ -674,7 +694,7 @@ class _ProgressYnab:
|
|
|
674
694
|
task_id: TaskID
|
|
675
695
|
|
|
676
696
|
@retry(stop=stop_after_attempt(3))
|
|
677
|
-
async def get
|
|
697
|
+
async def get(self, endpoint: Callable[..., Awaitable[_T]]) -> _T:
|
|
678
698
|
try:
|
|
679
699
|
return await endpoint(
|
|
680
700
|
plan_id=self.plan_id,
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.8
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://github.com/mxr/sqlite-export-for-ynab
|
|
8
|
-
Keywords:
|
|
8
|
+
Keywords: budget,cli,plan,sql,sqlite,ynab
|
|
9
9
|
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.
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
16
|
Requires-Dist: aiohttp>=3
|
|
17
17
|
Requires-Dist: aiopathlib
|
|
18
18
|
Requires-Dist: aiosqlite
|
|
19
|
-
Requires-Dist: asyncio-for-ynab~=1.
|
|
19
|
+
Requires-Dist: asyncio-for-ynab~=1.87.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
|
-
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
28
|
+
[](https://results.pre-commit.ci/latest/github/mxr/sqlite-export-for-ynab/main)
|
|
29
|
+
[](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
|
|
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
|
|
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
|
|
58
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
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
|
|
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,
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
File without changes
|
{sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/__init__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/__main__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/sqlite_export_for_ynab/ddl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_export_for_ynab-2.9.6 → sqlite_export_for_ynab-2.9.8}/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
|