sqlite-export-for-ynab 1.6.2__tar.gz → 2.0.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-1.6.2/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.0.0}/PKG-INFO +31 -30
  2. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/README.md +28 -27
  3. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/setup.cfg +3 -3
  4. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/_main.py +66 -62
  5. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/ddl/create-relations.sql +30 -28
  6. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/ddl/drop-relations.sql +1 -1
  7. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0/sqlite_export_for_ynab.egg-info}/PKG-INFO +31 -30
  8. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/testing/fixtures.py +10 -10
  9. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/tests/_main_test.py +57 -57
  10. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/LICENSE +0 -0
  11. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/pyproject.toml +0 -0
  12. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/setup.py +0 -0
  13. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/__init__.py +0 -0
  14. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/__main__.py +0 -0
  15. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
  16. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab/py.typed +0 -0
  17. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
  18. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab.egg-info/dependency_links.txt +0 -0
  19. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab.egg-info/entry_points.txt +0 -0
  20. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab.egg-info/requires.txt +0 -0
  21. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/sqlite_export_for_ynab.egg-info/top_level.txt +0 -0
  22. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/testing/__init__.py +0 -0
  23. {sqlite_export_for_ynab-1.6.2 → sqlite_export_for_ynab-2.0.0}/tests/__init__.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlite_export_for_ynab
3
- Version: 1.6.2
4
- Summary: SQLite Export for YNAB - Export YNAB Budget Data to SQLite
3
+ Version: 2.0.0
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
7
7
  Author-email: maxr@outlook.com
8
8
  License: MIT
9
- Keywords: ynab,sqlite,sql,budget,cli
9
+ Keywords: ynab,sqlite,sql,budget,plan,cli
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3 :: Only
12
12
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -26,7 +26,7 @@ SQLite Export for YNAB - Export YNAB Budget Data to SQLite
26
26
 
27
27
  ## What This Does
28
28
 
29
- Export your [YNAB](https://ynab.com/) budget to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your budget with any tools compatible with SQLite.
29
+ 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.
30
30
 
31
31
  ## Installation
32
32
 
@@ -44,7 +44,7 @@ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-t
44
44
  $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
45
45
  ```
46
46
 
47
- Run the tool from the terminal to download your budget:
47
+ Run the tool from the terminal to download your plans:
48
48
 
49
49
  ```console
50
50
  $ sqlite-export-for-ynab
@@ -80,7 +80,7 @@ asyncio.run(sync(token, db, full_refresh))
80
80
  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:
81
81
 
82
82
  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).
83
- 1. Foreign keys are added as needed (ex: budget ID, transaction ID) so data across budgets remains separate.
83
+ 1. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
84
84
  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 include fields to improve quality of life (ex: `amount_major` to convert from [YNAB's milliunits](https://api.ynab.com/#formats) to [major units](https://en.wikipedia.org/wiki/ISO_4217) i.e. dollars) and filter out deleted transactions/subtransactions.
85
85
 
86
86
  ## Querying
@@ -89,35 +89,35 @@ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` delib
89
89
 
90
90
  ### Sample Queries
91
91
 
92
- To get the top 5 payees by spending per budget, you could do:
92
+ To get the top 5 payees by spending per plan, you could do:
93
93
 
94
94
  ```sql
95
95
  WITH
96
96
  ranked_payees AS (
97
97
  SELECT
98
- b.name AS budget_name
98
+ pl."name" AS plan_name
99
99
  , t.payee_name AS payee
100
100
  , SUM(t.amount_major) AS net_spent
101
101
  , ROW_NUMBER() OVER (
102
102
  PARTITION BY
103
- b.id
103
+ pl.id
104
104
  ORDER BY
105
105
  SUM(t.amount) ASC
106
106
  ) AS rnk
107
107
  FROM
108
108
  flat_transactions AS t
109
- INNER JOIN budgets AS b
110
- ON t.budget_id = b.id
109
+ INNER JOIN plans AS pl
110
+ ON t.plan_id = pl.id
111
111
  WHERE
112
112
  t.payee_name != 'Starting Balance'
113
113
  AND t.transfer_account_id IS NULL
114
114
  GROUP BY
115
- b.id
115
+ pl.id
116
116
  , t.payee_id
117
117
  )
118
118
 
119
119
  SELECT
120
- budget_name
120
+ plan_name
121
121
  , payee
122
122
  , net_spent
123
123
  FROM
@@ -125,7 +125,7 @@ FROM
125
125
  WHERE
126
126
  rnk <= 5
127
127
  ORDER BY
128
- budget_name ASC
128
+ plan_name ASC
129
129
  , net_spent DESC
130
130
  ;
131
131
  ```
@@ -134,43 +134,44 @@ To get duplicate payees, or payees with no transactions:
134
134
 
135
135
  ```sql
136
136
  SELECT DISTINCT
137
- b.name AS budget
138
- , dupes.name AS payee
137
+ pl."name" AS "plan"
138
+ , dupes."name" AS payee
139
139
  FROM (
140
140
  SELECT DISTINCT
141
- p.budget_id
142
- , p.name
141
+ p.plan_id
142
+ , p."name"
143
143
  FROM payees AS p
144
144
  LEFT JOIN flat_transactions AS ft
145
145
  ON
146
- p.budget_id = ft.budget_id
146
+ p.plan_id = ft.plan_id
147
147
  AND p.id = ft.payee_id
148
148
  LEFT JOIN scheduled_flat_transactions AS sft
149
149
  ON
150
- p.budget_id = sft.budget_id
150
+ p.plan_id = sft.plan_id
151
151
  AND p.id = sft.payee_id
152
152
  WHERE
153
153
  TRUE
154
154
  AND ft.payee_id IS NULL
155
155
  AND sft.payee_id IS NULL
156
156
  AND p.transfer_account_id IS NULL
157
- AND p.name != 'Reconciliation Balance Adjustment'
157
+ AND p."name" != 'Reconciliation Balance Adjustment'
158
+ AND p."name" != 'Manual Balance Adjustment'
158
159
  AND NOT p.deleted
159
160
 
160
161
  UNION ALL
161
162
 
162
163
  SELECT
163
- budget_id
164
- , name
164
+ plan_id
165
+ , "name"
165
166
  FROM payees
166
167
  WHERE NOT deleted
167
- GROUP BY budget_id, name
168
+ GROUP BY plan_id, "name"
168
169
  HAVING COUNT(*) > 1
169
170
 
170
171
  ) AS dupes
171
- INNER JOIN budgets AS b
172
- ON dupes.budget_id = b.id
173
- ORDER BY budget, payee
172
+ INNER JOIN plans AS pl
173
+ ON dupes.plan_id = pl.id
174
+ ORDER BY "plan", payee
174
175
  ;
175
176
  ```
176
177
 
@@ -178,11 +179,11 @@ To count the spend for a category (ex: "Apps") between this month and the next 1
178
179
 
179
180
  ```sql
180
181
  SELECT
181
- budget_id
182
+ plan_id
182
183
  , SUM(amount_major) AS amount_major
183
184
  FROM (
184
185
  SELECT
185
- budget_id
186
+ plan_id
186
187
  , amount_major
187
188
  FROM flat_transactions
188
189
  WHERE
@@ -190,7 +191,7 @@ FROM (
190
191
  AND SUBSTR(`date`, 1, 7) = SUBSTR(DATE(), 1, 7)
191
192
  UNION ALL
192
193
  SELECT
193
- budget_id
194
+ plan_id
194
195
  , amount_major * (
195
196
  CASE
196
197
  WHEN frequency = 'monthly' THEN 11
@@ -6,7 +6,7 @@ SQLite Export for YNAB - Export YNAB Budget Data to SQLite
6
6
 
7
7
  ## What This Does
8
8
 
9
- Export your [YNAB](https://ynab.com/) budget to a local [SQLite](https://www.sqlite.org/) DB. Then you can query your budget with any tools compatible with SQLite.
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
10
 
11
11
  ## Installation
12
12
 
@@ -24,7 +24,7 @@ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-t
24
24
  $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
25
25
  ```
26
26
 
27
- Run the tool from the terminal to download your budget:
27
+ Run the tool from the terminal to download your plans:
28
28
 
29
29
  ```console
30
30
  $ sqlite-export-for-ynab
@@ -60,7 +60,7 @@ asyncio.run(sync(token, db, full_refresh))
60
60
  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:
61
61
 
62
62
  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).
63
- 1. Foreign keys are added as needed (ex: budget ID, transaction ID) so data across budgets remains separate.
63
+ 1. Foreign keys are added as needed (ex: plan ID, transaction ID) so data across plans remains separate.
64
64
  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 include fields to improve quality of life (ex: `amount_major` to convert from [YNAB's milliunits](https://api.ynab.com/#formats) to [major units](https://en.wikipedia.org/wiki/ISO_4217) i.e. dollars) and filter out deleted transactions/subtransactions.
65
65
 
66
66
  ## Querying
@@ -69,35 +69,35 @@ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` delib
69
69
 
70
70
  ### Sample Queries
71
71
 
72
- To get the top 5 payees by spending per budget, you could do:
72
+ To get the top 5 payees by spending per plan, you could do:
73
73
 
74
74
  ```sql
75
75
  WITH
76
76
  ranked_payees AS (
77
77
  SELECT
78
- b.name AS budget_name
78
+ pl."name" AS plan_name
79
79
  , t.payee_name AS payee
80
80
  , SUM(t.amount_major) AS net_spent
81
81
  , ROW_NUMBER() OVER (
82
82
  PARTITION BY
83
- b.id
83
+ pl.id
84
84
  ORDER BY
85
85
  SUM(t.amount) ASC
86
86
  ) AS rnk
87
87
  FROM
88
88
  flat_transactions AS t
89
- INNER JOIN budgets AS b
90
- ON t.budget_id = b.id
89
+ INNER JOIN plans AS pl
90
+ ON t.plan_id = pl.id
91
91
  WHERE
92
92
  t.payee_name != 'Starting Balance'
93
93
  AND t.transfer_account_id IS NULL
94
94
  GROUP BY
95
- b.id
95
+ pl.id
96
96
  , t.payee_id
97
97
  )
98
98
 
99
99
  SELECT
100
- budget_name
100
+ plan_name
101
101
  , payee
102
102
  , net_spent
103
103
  FROM
@@ -105,7 +105,7 @@ FROM
105
105
  WHERE
106
106
  rnk <= 5
107
107
  ORDER BY
108
- budget_name ASC
108
+ plan_name ASC
109
109
  , net_spent DESC
110
110
  ;
111
111
  ```
@@ -114,43 +114,44 @@ To get duplicate payees, or payees with no transactions:
114
114
 
115
115
  ```sql
116
116
  SELECT DISTINCT
117
- b.name AS budget
118
- , dupes.name AS payee
117
+ pl."name" AS "plan"
118
+ , dupes."name" AS payee
119
119
  FROM (
120
120
  SELECT DISTINCT
121
- p.budget_id
122
- , p.name
121
+ p.plan_id
122
+ , p."name"
123
123
  FROM payees AS p
124
124
  LEFT JOIN flat_transactions AS ft
125
125
  ON
126
- p.budget_id = ft.budget_id
126
+ p.plan_id = ft.plan_id
127
127
  AND p.id = ft.payee_id
128
128
  LEFT JOIN scheduled_flat_transactions AS sft
129
129
  ON
130
- p.budget_id = sft.budget_id
130
+ p.plan_id = sft.plan_id
131
131
  AND p.id = sft.payee_id
132
132
  WHERE
133
133
  TRUE
134
134
  AND ft.payee_id IS NULL
135
135
  AND sft.payee_id IS NULL
136
136
  AND p.transfer_account_id IS NULL
137
- AND p.name != 'Reconciliation Balance Adjustment'
137
+ AND p."name" != 'Reconciliation Balance Adjustment'
138
+ AND p."name" != 'Manual Balance Adjustment'
138
139
  AND NOT p.deleted
139
140
 
140
141
  UNION ALL
141
142
 
142
143
  SELECT
143
- budget_id
144
- , name
144
+ plan_id
145
+ , "name"
145
146
  FROM payees
146
147
  WHERE NOT deleted
147
- GROUP BY budget_id, name
148
+ GROUP BY plan_id, "name"
148
149
  HAVING COUNT(*) > 1
149
150
 
150
151
  ) AS dupes
151
- INNER JOIN budgets AS b
152
- ON dupes.budget_id = b.id
153
- ORDER BY budget, payee
152
+ INNER JOIN plans AS pl
153
+ ON dupes.plan_id = pl.id
154
+ ORDER BY "plan", payee
154
155
  ;
155
156
  ```
156
157
 
@@ -158,11 +159,11 @@ To count the spend for a category (ex: "Apps") between this month and the next 1
158
159
 
159
160
  ```sql
160
161
  SELECT
161
- budget_id
162
+ plan_id
162
163
  , SUM(amount_major) AS amount_major
163
164
  FROM (
164
165
  SELECT
165
- budget_id
166
+ plan_id
166
167
  , amount_major
167
168
  FROM flat_transactions
168
169
  WHERE
@@ -170,7 +171,7 @@ FROM (
170
171
  AND SUBSTR(`date`, 1, 7) = SUBSTR(DATE(), 1, 7)
171
172
  UNION ALL
172
173
  SELECT
173
- budget_id
174
+ plan_id
174
175
  , amount_major * (
175
176
  CASE
176
177
  WHEN frequency = 'monthly' THEN 11
@@ -1,7 +1,7 @@
1
1
  [metadata]
2
2
  name = sqlite_export_for_ynab
3
- version = 1.6.2
4
- description = SQLite Export for YNAB - Export YNAB Budget Data to SQLite
3
+ version = 2.0.0
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
7
7
  url = https://github.com/mxr/sqlite-export-for-ynab
@@ -14,7 +14,7 @@ classifiers =
14
14
  Programming Language :: Python :: 3 :: Only
15
15
  Programming Language :: Python :: Implementation :: CPython
16
16
  Programming Language :: Python :: Implementation :: PyPy
17
- keywords = ynab, sqlite, sql, budget, cli
17
+ keywords = ynab, sqlite, sql, budget, plan, cli
18
18
 
19
19
  [options]
20
20
  packages = find:
@@ -41,7 +41,7 @@ _EntryTable = (
41
41
  | Literal["scheduled_subtransactions"]
42
42
  )
43
43
  _ALL_RELATIONS = frozenset(
44
- ("budgets", "flat_transactions", "scheduled_flat_transactions")
44
+ ("plans", "flat_transactions", "scheduled_flat_transactions")
45
45
  + tuple(lit.__args__[0] for lit in _EntryTable.__args__)
46
46
  )
47
47
 
@@ -61,7 +61,7 @@ async def async_main(argv: Sequence[str] | None = None) -> int:
61
61
  parser.add_argument(
62
62
  "--full-refresh",
63
63
  action="store_true",
64
- help="**DROP ALL TABLES** and fetch all budget data again.",
64
+ help="**DROP ALL TABLES** and fetch all data again.",
65
65
  )
66
66
  parser.add_argument(
67
67
  "--version", action="version", version=f"%(prog)s {version(_PACKAGE)}"
@@ -98,9 +98,9 @@ def default_db_path() -> Path:
98
98
 
99
99
  async def sync(token: str, db: Path, full_refresh: bool) -> None:
100
100
  async with aiohttp.ClientSession() as session:
101
- budgets = (await YnabClient(token, session)("budgets"))["budgets"]
101
+ plans = (await YnabClient(token, session)("plans"))["plans"]
102
102
 
103
- budget_ids = [b["id"] for b in budgets]
103
+ plan_ids = [plan["id"] for plan in plans]
104
104
 
105
105
  if not db.exists():
106
106
  db.parent.mkdir(parents=True, exist_ok=True)
@@ -122,17 +122,17 @@ async def sync(token: str, db: Path, full_refresh: bool) -> None:
122
122
  con.commit()
123
123
  print("Done")
124
124
 
125
- print("Fetching budget data...")
125
+ print("Fetching plan data...")
126
126
  lkos = get_last_knowledge_of_server(cur)
127
127
  async with aiohttp.ClientSession() as session:
128
- with tldm(desc="Budget Data", total=len(budgets) * 5) as pbar:
128
+ with tldm(desc="Plan Data", total=len(plans) * 5) as pbar:
129
129
  yc = ProgressYnabClient(YnabClient(token, session), pbar)
130
130
 
131
- account_jobs = jobs(yc, "accounts", budget_ids, lkos)
132
- cat_jobs = jobs(yc, "categories", budget_ids, lkos)
133
- payee_jobs = jobs(yc, "payees", budget_ids, lkos)
134
- txn_jobs = jobs(yc, "transactions", budget_ids, lkos)
135
- sched_txn_jobs = jobs(yc, "scheduled_transactions", budget_ids, lkos)
131
+ account_jobs = jobs(yc, "accounts", plan_ids, lkos)
132
+ cat_jobs = jobs(yc, "categories", plan_ids, lkos)
133
+ payee_jobs = jobs(yc, "payees", plan_ids, lkos)
134
+ txn_jobs = jobs(yc, "transactions", plan_ids, lkos)
135
+ sched_txn_jobs = jobs(yc, "scheduled_transactions", plan_ids, lkos)
136
136
 
137
137
  data = await asyncio.gather(
138
138
  *account_jobs, *cat_jobs, *payee_jobs, *txn_jobs, *sched_txn_jobs
@@ -150,8 +150,10 @@ async def sync(token: str, db: Path, full_refresh: bool) -> None:
150
150
  all_sched_txn_data = data[la + lc + lp + lt :]
151
151
 
152
152
  new_lkos = {
153
- bid: t["server_knowledge"]
154
- for bid, t in zip(budget_ids, all_txn_data, strict=True)
153
+ plan_id: transaction_data["server_knowledge"]
154
+ for plan_id, transaction_data in zip(
155
+ plan_ids, all_txn_data, strict=True
156
+ )
155
157
  }
156
158
  print("Done")
157
159
 
@@ -164,19 +166,21 @@ async def sync(token: str, db: Path, full_refresh: bool) -> None:
164
166
  ):
165
167
  print("No new data fetched")
166
168
  else:
167
- print("Inserting budget data...")
168
- insert_budgets(cur, budgets, new_lkos)
169
- for bid, account_data in zip(budget_ids, all_account_data, strict=True):
170
- insert_accounts(cur, bid, account_data["accounts"])
171
- for bid, cat_data in zip(budget_ids, all_cat_data, strict=True):
172
- insert_category_groups(cur, bid, cat_data["category_groups"])
173
- for bid, payee_data in zip(budget_ids, all_payee_data, strict=True):
174
- insert_payees(cur, bid, payee_data["payees"])
175
- for bid, txn_data in zip(budget_ids, all_txn_data, strict=True):
176
- insert_transactions(cur, bid, txn_data["transactions"])
177
- for bid, sched_txn_data in zip(budget_ids, all_sched_txn_data, strict=True):
169
+ print("Inserting plan data...")
170
+ insert_plans(cur, plans, new_lkos)
171
+ for plan_id, account_data in zip(plan_ids, all_account_data, strict=True):
172
+ insert_accounts(cur, plan_id, account_data["accounts"])
173
+ for plan_id, cat_data in zip(plan_ids, all_cat_data, strict=True):
174
+ insert_category_groups(cur, plan_id, cat_data["category_groups"])
175
+ for plan_id, payee_data in zip(plan_ids, all_payee_data, strict=True):
176
+ insert_payees(cur, plan_id, payee_data["payees"])
177
+ for plan_id, txn_data in zip(plan_ids, all_txn_data, strict=True):
178
+ insert_transactions(cur, plan_id, txn_data["transactions"])
179
+ for plan_id, sched_txn_data in zip(
180
+ plan_ids, all_sched_txn_data, strict=True
181
+ ):
178
182
  insert_scheduled_transactions(
179
- cur, bid, sched_txn_data["scheduled_transactions"]
183
+ cur, plan_id, sched_txn_data["scheduled_transactions"]
180
184
  )
181
185
  print("Done")
182
186
 
@@ -202,17 +206,17 @@ def get_last_knowledge_of_server(cur: sqlite3.Cursor) -> dict[str, int]:
202
206
  return {
203
207
  r["id"]: r["last_knowledge_of_server"]
204
208
  for r in cur.execute(
205
- "SELECT id, last_knowledge_of_server FROM budgets",
209
+ "SELECT id, last_knowledge_of_server FROM plans",
206
210
  ).fetchall()
207
211
  }
208
212
 
209
213
 
210
- def insert_budgets(
211
- cur: sqlite3.Cursor, budgets: list[dict[str, Any]], lkos: dict[str, int]
214
+ def insert_plans(
215
+ cur: sqlite3.Cursor, plans: list[dict[str, Any]], lkos: dict[str, int]
212
216
  ) -> None:
213
217
  cur.executemany(
214
218
  """
215
- INSERT OR REPLACE INTO budgets (
219
+ INSERT OR REPLACE INTO plans (
216
220
  id
217
221
  , name
218
222
  , currency_format_currency_symbol
@@ -227,18 +231,18 @@ def insert_budgets(
227
231
  """,
228
232
  (
229
233
  (
230
- bid := b["id"],
231
- b["name"],
232
- b["currency_format"]["currency_symbol"],
233
- b["currency_format"]["decimal_digits"],
234
- b["currency_format"]["decimal_separator"],
235
- b["currency_format"]["display_symbol"],
236
- b["currency_format"]["group_separator"],
237
- b["currency_format"]["iso_code"],
238
- b["currency_format"]["symbol_first"],
239
- lkos[bid],
234
+ plan_id := plan["id"],
235
+ plan["name"],
236
+ plan["currency_format"]["currency_symbol"],
237
+ plan["currency_format"]["decimal_digits"],
238
+ plan["currency_format"]["decimal_separator"],
239
+ plan["currency_format"]["display_symbol"],
240
+ plan["currency_format"]["group_separator"],
241
+ plan["currency_format"]["iso_code"],
242
+ plan["currency_format"]["symbol_first"],
243
+ lkos[plan_id],
240
244
  )
241
- for b in budgets
245
+ for plan in plans
242
246
  ),
243
247
  )
244
248
 
@@ -249,7 +253,7 @@ _LOAN_ACCOUNT_PERIODIC_VALUES = frozenset(
249
253
 
250
254
 
251
255
  def insert_accounts(
252
- cur: sqlite3.Cursor, budget_id: str, accounts: list[dict[str, Any]]
256
+ cur: sqlite3.Cursor, plan_id: str, accounts: list[dict[str, Any]]
253
257
  ) -> None:
254
258
  # YNAB's LoanAccountPeriodValues are untyped dicts so we need to turn them into a more standard sub-entry view
255
259
  updated_accounts = [
@@ -271,7 +275,7 @@ def insert_accounts(
271
275
 
272
276
  return insert_nested_entries(
273
277
  cur,
274
- budget_id,
278
+ plan_id,
275
279
  updated_accounts,
276
280
  "Accounts",
277
281
  "accounts",
@@ -281,11 +285,11 @@ def insert_accounts(
281
285
 
282
286
 
283
287
  def insert_category_groups(
284
- cur: sqlite3.Cursor, budget_id: str, category_groups: list[dict[str, Any]]
288
+ cur: sqlite3.Cursor, plan_id: str, category_groups: list[dict[str, Any]]
285
289
  ) -> None:
286
290
  return insert_nested_entries(
287
291
  cur,
288
- budget_id,
292
+ plan_id,
289
293
  category_groups,
290
294
  "Categories",
291
295
  "category_groups",
@@ -295,21 +299,21 @@ def insert_category_groups(
295
299
 
296
300
 
297
301
  def insert_payees(
298
- cur: sqlite3.Cursor, budget_id: str, payees: list[dict[str, Any]]
302
+ cur: sqlite3.Cursor, plan_id: str, payees: list[dict[str, Any]]
299
303
  ) -> None:
300
304
  if not payees:
301
305
  return
302
306
 
303
307
  for payee in tldm(payees, desc="Payees"):
304
- insert_entry(cur, "payees", budget_id, payee)
308
+ insert_entry(cur, "payees", plan_id, payee)
305
309
 
306
310
 
307
311
  def insert_transactions(
308
- cur: sqlite3.Cursor, budget_id: str, transactions: list[dict[str, Any]]
312
+ cur: sqlite3.Cursor, plan_id: str, transactions: list[dict[str, Any]]
309
313
  ) -> None:
310
314
  return insert_nested_entries(
311
315
  cur,
312
- budget_id,
316
+ plan_id,
313
317
  transactions,
314
318
  "Transactions",
315
319
  "transactions",
@@ -319,11 +323,11 @@ def insert_transactions(
319
323
 
320
324
 
321
325
  def insert_scheduled_transactions(
322
- cur: sqlite3.Cursor, budget_id: str, scheduled_transactions: list[dict[str, Any]]
326
+ cur: sqlite3.Cursor, plan_id: str, scheduled_transactions: list[dict[str, Any]]
323
327
  ) -> None:
324
328
  return insert_nested_entries(
325
329
  cur,
326
- budget_id,
330
+ plan_id,
327
331
  scheduled_transactions,
328
332
  "Scheduled Transactions",
329
333
  "scheduled_transactions",
@@ -335,7 +339,7 @@ def insert_scheduled_transactions(
335
339
  @overload
336
340
  def insert_nested_entries(
337
341
  cur: sqlite3.Cursor,
338
- budget_id: str,
342
+ plan_id: str,
339
343
  entries: list[dict[str, Any]],
340
344
  desc: Literal["Accounts"],
341
345
  entries_name: Literal["accounts"],
@@ -347,7 +351,7 @@ def insert_nested_entries(
347
351
  @overload
348
352
  def insert_nested_entries(
349
353
  cur: sqlite3.Cursor,
350
- budget_id: str,
354
+ plan_id: str,
351
355
  entries: list[dict[str, Any]],
352
356
  desc: Literal["Categories"],
353
357
  entries_name: Literal["category_groups"],
@@ -359,7 +363,7 @@ def insert_nested_entries(
359
363
  @overload
360
364
  def insert_nested_entries(
361
365
  cur: sqlite3.Cursor,
362
- budget_id: str,
366
+ plan_id: str,
363
367
  entries: list[dict[str, Any]],
364
368
  desc: Literal["Transactions"],
365
369
  entries_name: Literal["transactions"],
@@ -371,7 +375,7 @@ def insert_nested_entries(
371
375
  @overload
372
376
  def insert_nested_entries(
373
377
  cur: sqlite3.Cursor,
374
- budget_id: str,
378
+ plan_id: str,
375
379
  entries: list[dict[str, Any]],
376
380
  desc: Literal["Scheduled Transactions"],
377
381
  entries_name: Literal["scheduled_transactions"],
@@ -382,7 +386,7 @@ def insert_nested_entries(
382
386
 
383
387
  def insert_nested_entries(
384
388
  cur: sqlite3.Cursor,
385
- budget_id: str,
389
+ plan_id: str,
386
390
  entries: list[dict[str, Any]],
387
391
  desc: (
388
392
  Literal["Accounts"]
@@ -419,24 +423,24 @@ def insert_nested_entries(
419
423
  insert_entry(
420
424
  cur,
421
425
  entries_name,
422
- budget_id,
426
+ plan_id,
423
427
  {k: v for k, v in entry.items() if k != subentries_name},
424
428
  )
425
429
  pbar.update()
426
430
 
427
431
  for subentry in entry[subentries_name]:
428
- insert_entry(cur, subentries_table_name, budget_id, subentry)
432
+ insert_entry(cur, subentries_table_name, plan_id, subentry)
429
433
  pbar.update()
430
434
 
431
435
 
432
436
  def insert_entry(
433
437
  cur: sqlite3.Cursor,
434
438
  table: _EntryTable,
435
- budget_id: str,
439
+ plan_id: str,
436
440
  entry: dict[str, Any],
437
441
  ) -> None:
438
442
  ekeys, evalues = zip(*entry.items(), strict=True)
439
- keys, values = ekeys + ("budget_id",), evalues + (budget_id,)
443
+ keys, values = ekeys + ("plan_id",), evalues + (plan_id,)
440
444
 
441
445
  cur.execute(
442
446
  f"INSERT OR REPLACE INTO {table} ({', '.join(keys)}) VALUES ({', '.join('?' * len(values))})",
@@ -453,12 +457,12 @@ def jobs(
453
457
  | Literal["transactions"]
454
458
  | Literal["scheduled_transactions"]
455
459
  ),
456
- budget_ids: list[str],
460
+ plan_ids: list[str],
457
461
  lkos: dict[str, int],
458
462
  ) -> list[Awaitable[dict[str, Any]]]:
459
463
  return [
460
- yc(f"budgets/{bid}/{endpoint}", last_knowledge_of_server=lkos.get(bid))
461
- for bid in budget_ids
464
+ yc(f"plans/{plan_id}/{endpoint}", last_knowledge_of_server=lkos.get(plan_id))
465
+ for plan_id in plan_ids
462
466
  ]
463
467
 
464
468