sqlite-export-for-ynab 2.9.1__py3-none-any.whl

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.
@@ -0,0 +1,594 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlite_export_for_ynab
3
+ Version: 2.9.1
4
+ Summary: SQLite Export for YNAB - Export YNAB Data to SQLite
5
+ Author-email: Max R <mxr@users.noreply.github.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mxr/sqlite-export-for-ynab
8
+ Keywords: ynab,sqlite,sql,budget,plan,cli
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: Implementation :: CPython
12
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: aiohttp>=3
17
+ Requires-Dist: aiopathlib
18
+ Requires-Dist: aiosqlite
19
+ Requires-Dist: asyncio-for-ynab~=1.84.0
20
+ Requires-Dist: fasteners
21
+ Requires-Dist: rich>=10
22
+ Requires-Dist: tenacity
23
+ Dynamic: license-file
24
+
25
+ # sqlite-export-for-ynab
26
+
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
+
29
+ SQLite Export for YNAB - Export YNAB Budget Data to SQLite
30
+
31
+ ## What This Does
32
+
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.
34
+
35
+ ## Installation
36
+
37
+ ```console
38
+ $ pip install sqlite-export-for-ynab
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### CLI
44
+
45
+ Provision a [YNAB Personal Access Token](https://api.ynab.com/#personal-access-tokens) and save it as an environment variable.
46
+
47
+ ```console
48
+ $ export YNAB_PERSONAL_ACCESS_TOKEN="..."
49
+ ```
50
+
51
+ Run the tool from the terminal to download your plans:
52
+
53
+ ```console
54
+ $ sqlite-export-for-ynab
55
+ ```
56
+
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.
59
+
60
+ <a id="db-path"></a>You can specify the DB path with the following options
61
+ 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`.
64
+
65
+ ### Library
66
+
67
+ The library exposes the package `sqlite_export_for_ynab` and two functions - `default_db_path` and `sync`. You can use them as follows:
68
+
69
+ ```python
70
+ import asyncio
71
+ import os
72
+
73
+ from sqlite_export_for_ynab import default_db_path
74
+ from sqlite_export_for_ynab import sync
75
+
76
+ db = default_db_path()
77
+ token = os.environ["YNAB_PERSONAL_ACCESS_TOKEN"]
78
+ full_refresh = False
79
+
80
+ asyncio.run(sync(token, db, full_refresh))
81
+ ```
82
+
83
+ ## Relations
84
+
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:
86
+
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.
90
+
91
+ ## Querying
92
+
93
+ You can issue queries with typical SQLite tools. *`sqlite-export-for-ynab` deliberately does not implement a SQL REPL.*
94
+
95
+ ### Sample Queries
96
+
97
+ You can run the queries from this README using a tool like [`mdq`](https://github.com/yshavit/mdq). For example:
98
+
99
+ ```console
100
+ $ mdq '```sql dupes' path/to/sqlite-export-for-ynab/README.md -o plain \
101
+ | sqlite3 path/to/sqlite-export-for-ynab/db.sqlite
102
+ ```
103
+
104
+ The DB path is documented [above](#db-path).
105
+
106
+ To get the top 5 payees by spending per plan, you could do:
107
+
108
+ ```sql
109
+ WITH ranked_payees AS (
110
+ SELECT
111
+ pl.name AS plan_name
112
+ , t.payee_name AS payee
113
+ , SUM(t.amount_currency) AS net_spent
114
+ , ROW_NUMBER()
115
+ OVER (PARTITION BY pl.id ORDER BY SUM(t.amount) ASC)
116
+ AS rnk
117
+ FROM flat_transactions AS t INNER JOIN plans AS pl ON t.plan_id = pl.id
118
+ WHERE
119
+ t.payee_name != 'Starting Balance' AND t.transfer_account_id IS NULL
120
+ GROUP BY pl.id, t.payee_id
121
+ )
122
+
123
+ SELECT
124
+ plan_name
125
+ , payee
126
+ , net_spent
127
+ FROM ranked_payees
128
+ WHERE rnk <= 5
129
+ ORDER BY plan_name ASC, net_spent DESC
130
+ ;
131
+ ```
132
+
133
+ To get duplicate payees, or payees with no transactions:
134
+
135
+ ```sql
136
+ WITH used_payees AS (
137
+ SELECT
138
+ plan_id
139
+ , payee_id
140
+ FROM transactions
141
+ WHERE
142
+ TRUE
143
+ AND approved
144
+ AND payee_id IS NOT NULL
145
+ AND NOT deleted
146
+ UNION
147
+ SELECT
148
+ plan_id
149
+ , payee_id
150
+ FROM subtransactions
151
+ WHERE
152
+ TRUE
153
+ AND payee_id IS NOT NULL
154
+ AND NOT deleted
155
+ UNION
156
+ SELECT
157
+ plan_id
158
+ , payee_id
159
+ FROM scheduled_transactions
160
+ WHERE
161
+ TRUE
162
+ AND payee_id IS NOT NULL
163
+ AND NOT deleted
164
+ UNION
165
+ SELECT
166
+ plan_id
167
+ , payee_id
168
+ FROM scheduled_subtransactions
169
+ WHERE
170
+ TRUE
171
+ AND payee_id IS NOT NULL
172
+ AND NOT deleted
173
+ )
174
+
175
+ SELECT
176
+ pl.name AS "plan"
177
+ , dupes.name AS payee
178
+ FROM (
179
+ SELECT
180
+ p.plan_id
181
+ , p.name
182
+ FROM payees AS p
183
+ LEFT JOIN used_payees AS up ON p.plan_id = up.plan_id AND p.id = up.payee_id
184
+ WHERE
185
+ TRUE
186
+ AND up.payee_id IS NULL
187
+ AND p.transfer_account_id IS NULL
188
+ AND p.name != 'Reconciliation Balance Adjustment'
189
+ AND p.name != 'Manual Balance Adjustment'
190
+ AND NOT p.deleted
191
+ UNION
192
+ SELECT
193
+ plan_id
194
+ , name
195
+ FROM payees
196
+ WHERE NOT deleted
197
+ GROUP BY plan_id, name
198
+ HAVING COUNT(*) > 1
199
+ ) AS dupes
200
+ INNER JOIN plans AS pl ON dupes.plan_id = pl.id
201
+ ORDER BY "plan", payee
202
+ ;
203
+ ```
204
+
205
+ To count the spend for a category (ex: "Apps") between this month and the next 11 months (inclusive):
206
+
207
+ ```sql
208
+ SELECT
209
+ plan_id
210
+ , SUM(amount_currency) AS amount_currency
211
+ FROM (
212
+ SELECT
213
+ plan_id
214
+ , amount_currency
215
+ FROM flat_transactions
216
+ WHERE
217
+ category_name = 'Apps'
218
+ AND SUBSTR("date", 1, 7) = SUBSTR(DATE(), 1, 7)
219
+ UNION ALL
220
+ SELECT
221
+ plan_id
222
+ , amount_currency * (
223
+ CASE
224
+ WHEN frequency = 'monthly' THEN 11
225
+ ELSE 1 -- assumes yearly
226
+ END
227
+ ) AS amount_currency
228
+ FROM scheduled_flat_transactions
229
+ WHERE
230
+ category_name = 'Apps'
231
+ AND SUBSTR(date_next, 1, 7) < SUBSTR(DATE('now', '+1 year'), 1, 7)
232
+ ) AS spend
233
+ ;
234
+ ```
235
+
236
+ To estimate taxable interest for a given year[^1]:
237
+
238
+ ```sql
239
+ -- Parameters expected by this query:
240
+ -- @tax_rate
241
+ -- @year
242
+ -- @plan_id (optional, defaults to output for all plans)
243
+ -- @estimated_additional_interest (optional,
244
+ -- estimated interest not in YNAB such as investment income)
245
+ -- @interest_reporting_threshold (optional, defaults to the $10
246
+ -- common threshold, but confirm with actual documents)
247
+ -- @interest_payee_name (optional, defaults to Interest)
248
+ --
249
+ -- Example with only required params:
250
+ -- sqlite3 -header -box path/to/db.sqlite \
251
+ -- -cmd '.parameter init' \
252
+ -- -cmd ".parameter set @tax_rate 0.25" \
253
+ -- -cmd ".parameter set @year 2025" \
254
+ -- < query.sql
255
+ --
256
+ -- Example with all params:
257
+ -- -cmd ".parameter set @tax_rate 0.25" \
258
+ -- -cmd ".parameter set @year 2025" \
259
+ -- -cmd ".parameter set @estimated_additional_interest 250.00" \
260
+ -- -cmd ".parameter set @interest_reporting_threshold 10" \
261
+ -- -cmd ".parameter set @interest_payee_name Interest" \
262
+ -- -cmd ".parameter set @plan_id your-plan-id" \
263
+ -- < query.sql
264
+
265
+ WITH interest_by_account AS (
266
+ SELECT
267
+ plan_id
268
+ , account_name
269
+ , SUM(amount_currency) AS total
270
+ FROM flat_transactions
271
+ WHERE
272
+ TRUE
273
+ AND payee_name = COALESCE(NULLIF(@interest_payee_name, ''), 'Interest')
274
+ AND SUBSTR("date", 1, 4) = CAST(@year AS TEXT)
275
+ AND (COALESCE(@plan_id, '') = '' OR plan_id = @plan_id)
276
+ GROUP BY plan_id, account_name
277
+ HAVING total >= CAST(COALESCE(@interest_reporting_threshold, 10) AS REAL)
278
+ )
279
+
280
+ , interest_by_plan AS (
281
+ SELECT
282
+ plans.id AS plan_id
283
+ , plans.name AS plan_name
284
+ , COALESCE(SUM(interest_by_account.total), 0) AS interest_in_ynab
285
+ FROM plans
286
+ LEFT JOIN interest_by_account ON plans.id = interest_by_account.plan_id
287
+ WHERE COALESCE(@plan_id, '') = '' OR plans.id = @plan_id
288
+ GROUP BY plan_id, plan_name
289
+ )
290
+
291
+ , ranked_interest AS (
292
+ SELECT
293
+ plan_id
294
+ , plan_name
295
+ , interest_in_ynab
296
+ , interest_in_ynab
297
+ + CAST(COALESCE(@estimated_additional_interest, 0) AS REAL)
298
+ AS interest_with_estimate
299
+ , ROW_NUMBER() OVER (ORDER BY plan_name, plan_id) AS row_num
300
+ FROM interest_by_plan
301
+ )
302
+
303
+ , estimated_interest AS (
304
+ SELECT
305
+ plan_id
306
+ , plan_name
307
+ , interest_in_ynab
308
+ -- Additional interest is per-tax-return not per-YNAB-plan. Only add
309
+ -- additional interest to one plan's output to avoid double counting.
310
+ , CASE
311
+ WHEN row_num != 1 THEN interest_in_ynab
312
+ WHEN
313
+ interest_with_estimate
314
+ < CAST(COALESCE(@interest_reporting_threshold, 10) AS REAL)
315
+ THEN 0
316
+ ELSE interest_with_estimate
317
+ END AS estimated_total_taxable_interest
318
+ FROM ranked_interest
319
+ )
320
+
321
+ SELECT
322
+ plan_name AS "plan"
323
+ , PRINTF('%.2f', interest_in_ynab) AS interest_in_ynab
324
+ , PRINTF('%.2f', estimated_total_taxable_interest)
325
+ AS estimated_total_taxable_interest
326
+ , PRINTF(
327
+ '%.2f'
328
+ , estimated_total_taxable_interest * CAST(NULLIF(@tax_rate, '') AS REAL)
329
+ ) AS estimated_tax_liability
330
+ FROM estimated_interest
331
+ ORDER BY plan_name, plan_id
332
+ ;
333
+ ```
334
+
335
+ To compare assigned category values to a given account's balance:
336
+
337
+ ```sql
338
+ -- Parameters expected by this query:
339
+ -- @account_name_like (required, the account name to match against)
340
+ -- @plan_id (optional, defaults to output for all matching plans)
341
+ -- @include_category_groups
342
+ -- (optional, comma-separated category-group names to include;
343
+ -- exclusive with @exclude_category_groups)
344
+ -- @exclude_category_groups
345
+ -- (optional, comma-separated category-group names to exclude;
346
+ -- exclusive with @include_category_groups)
347
+ --
348
+ -- Example:
349
+ -- sqlite -header -box path/to/db.sqlite \
350
+ -- -cmd '.parameter init' \
351
+ -- -cmd ".parameter set @account_name_like %Savings%" \
352
+ -- -cmd ".parameter set @include_category_groups 'Home,Food'" \
353
+ -- < query.sql
354
+ CREATE TEMP TABLE excess_query_results AS
355
+ WITH params AS (
356
+ SELECT
357
+ TRIM(COALESCE(@account_name_like, '')) AS account_name_like
358
+ , TRIM(COALESCE(@plan_id, '')) AS plan_id
359
+ , TRIM(COALESCE(@include_category_groups, ''))
360
+ AS include_category_groups
361
+ , TRIM(COALESCE(@exclude_category_groups, ''))
362
+ AS exclude_category_groups
363
+ )
364
+
365
+ , scoped_plans AS (
366
+ SELECT
367
+ p.id
368
+ , p.name
369
+ FROM plans AS p
370
+ CROSS JOIN params AS prm
371
+ WHERE prm.plan_id = '' OR p.id = prm.plan_id
372
+ )
373
+
374
+ , split_include_category_groups (value, rest) AS (
375
+ SELECT
376
+ ''
377
+ , prm.include_category_groups || ','
378
+ FROM params AS prm
379
+ UNION ALL
380
+ SELECT
381
+ TRIM(SUBSTR(rest, 1, INSTR(rest, ',') - 1))
382
+ , SUBSTR(rest, INSTR(rest, ',') + 1)
383
+ FROM split_include_category_groups
384
+ WHERE rest != ''
385
+ )
386
+
387
+ , include_category_groups AS (
388
+ SELECT value AS name
389
+ FROM split_include_category_groups
390
+ WHERE value != ''
391
+ )
392
+
393
+ , split_exclude_category_groups (value, rest) AS (
394
+ SELECT
395
+ ''
396
+ , prm.exclude_category_groups || ','
397
+ FROM params AS prm
398
+ UNION ALL
399
+ SELECT
400
+ TRIM(SUBSTR(rest, 1, INSTR(rest, ',') - 1))
401
+ , SUBSTR(rest, INSTR(rest, ',') + 1)
402
+ FROM split_exclude_category_groups
403
+ WHERE rest != ''
404
+ )
405
+
406
+ , exclude_category_groups AS (
407
+ SELECT value AS name
408
+ FROM split_exclude_category_groups
409
+ WHERE value != ''
410
+ )
411
+
412
+ , matching_accounts AS (
413
+ SELECT
414
+ sp.id AS plan_id
415
+ , sp.name AS plan_name
416
+ , COUNT(*) AS matches
417
+ FROM scoped_plans AS sp
418
+ INNER JOIN accounts AS a ON sp.id = a.plan_id
419
+ CROSS JOIN params AS prm
420
+ WHERE NOT a.deleted AND a.name LIKE prm.account_name_like
421
+ GROUP BY sp.id, sp.name
422
+ )
423
+
424
+ , validation AS (
425
+ SELECT
426
+ p.account_name_like
427
+ , p.plan_id
428
+ , p.include_category_groups
429
+ , p.exclude_category_groups
430
+ FROM params AS p
431
+ )
432
+
433
+ , validation_errors AS (
434
+ SELECT 'Set @account_name_like' AS error
435
+ FROM validation AS v
436
+ WHERE v.account_name_like = ''
437
+ UNION ALL
438
+ SELECT
439
+ 'Set only one of @include_category_groups'
440
+ || ' or @exclude_category_groups' AS error
441
+ FROM validation AS v
442
+ WHERE v.include_category_groups != '' AND v.exclude_category_groups != ''
443
+ UNION ALL
444
+ SELECT 'No plan matched @plan_id' AS error
445
+ FROM validation AS v
446
+ WHERE
447
+ v.plan_id != '' AND NOT EXISTS (
448
+ SELECT 1
449
+ FROM scoped_plans
450
+ )
451
+ UNION ALL
452
+ SELECT 'No account names matched @account_name_like' AS error
453
+ FROM validation AS v
454
+ WHERE
455
+ v.account_name_like != '' AND NOT EXISTS (
456
+ SELECT 1
457
+ FROM matching_accounts
458
+ )
459
+ UNION ALL
460
+ SELECT
461
+ 'Matched more than 1 account in plan: '
462
+ || ma.plan_name AS error
463
+ FROM matching_accounts AS ma
464
+ WHERE ma.matches > 1
465
+ UNION ALL
466
+ SELECT
467
+ 'Unknown include category group in plan '
468
+ || sp.name
469
+ || ': '
470
+ || icg.name AS error
471
+ FROM scoped_plans AS sp
472
+ CROSS JOIN include_category_groups AS icg
473
+ LEFT JOIN category_groups AS cg
474
+ ON
475
+ sp.id = cg.plan_id
476
+ AND NOT COALESCE(cg.deleted, 0)
477
+ AND LOWER(cg.name) = LOWER(icg.name)
478
+ WHERE cg.id IS NULL
479
+ UNION ALL
480
+ SELECT
481
+ 'Unknown exclude category group in plan '
482
+ || sp.name
483
+ || ': '
484
+ || ecg.name AS error
485
+ FROM scoped_plans AS sp
486
+ CROSS JOIN exclude_category_groups AS ecg
487
+ LEFT JOIN category_groups AS cg
488
+ ON
489
+ sp.id = cg.plan_id
490
+ AND NOT COALESCE(cg.deleted, 0)
491
+ AND LOWER(cg.name) = LOWER(ecg.name)
492
+ WHERE cg.id IS NULL
493
+ )
494
+
495
+ , valid_params AS (
496
+ SELECT
497
+ v.account_name_like
498
+ , v.plan_id
499
+ , v.include_category_groups
500
+ , v.exclude_category_groups
501
+ FROM validation AS v
502
+ WHERE NOT EXISTS (
503
+ SELECT 1
504
+ FROM validation_errors
505
+ )
506
+ )
507
+
508
+ , matched_accounts AS (
509
+ SELECT
510
+ p.id AS plan_id
511
+ , p.name AS plan_name
512
+ , a.name AS account_name
513
+ , a.cleared_balance / 1000.0 AS account_amount
514
+ FROM plans AS p
515
+ INNER JOIN accounts AS a ON p.id = a.plan_id
516
+ CROSS JOIN valid_params AS v
517
+ WHERE
518
+ TRUE
519
+ AND NOT a.deleted
520
+ AND a.name LIKE v.account_name_like
521
+ AND (v.plan_id = '' OR p.id = v.plan_id)
522
+ )
523
+
524
+ , category_totals AS (
525
+ SELECT
526
+ c.plan_id
527
+ , COALESCE(SUM(c.balance), 0) / 1000.0 AS total
528
+ FROM categories AS c CROSS JOIN valid_params AS v
529
+ WHERE
530
+ TRUE
531
+ AND NOT c.deleted
532
+ AND c.category_group_name != 'Credit Card Payments'
533
+ AND NOT c.internal
534
+ AND (
535
+ v.include_category_groups = ''
536
+ OR EXISTS (
537
+ SELECT 1
538
+ FROM include_category_groups AS icg
539
+ WHERE LOWER(icg.name) = LOWER(c.category_group_name)
540
+ )
541
+ )
542
+ AND (
543
+ v.exclude_category_groups = ''
544
+ OR NOT EXISTS (
545
+ SELECT 1
546
+ FROM exclude_category_groups AS ecg
547
+ WHERE LOWER(ecg.name) = LOWER(c.category_group_name)
548
+ )
549
+ )
550
+ AND (v.plan_id = '' OR c.plan_id = v.plan_id)
551
+ GROUP BY c.plan_id
552
+ )
553
+
554
+ SELECT
555
+ ve.error AS error_message
556
+ , NULL AS "plan"
557
+ , NULL AS account
558
+ , NULL AS total
559
+ , NULL AS excess
560
+ FROM validation_errors AS ve
561
+
562
+ UNION ALL
563
+
564
+ SELECT
565
+ NULL AS error_message
566
+ , ma.plan_name AS "plan"
567
+ , ma.account_name AS account
568
+ , PRINTF('%.2f', COALESCE(ct.total, 0)) AS total
569
+ , PRINTF('%.2f', ma.account_amount - COALESCE(ct.total, 0)) AS excess
570
+ FROM matched_accounts AS ma
571
+ LEFT JOIN category_totals AS ct ON ma.plan_id = ct.plan_id
572
+ ;
573
+
574
+ SELECT error_message
575
+ FROM excess_query_results
576
+ WHERE error_message IS NOT NULL
577
+ ;
578
+
579
+ SELECT
580
+ eqr."plan"
581
+ , eqr.account
582
+ , eqr.total
583
+ , eqr.excess
584
+ FROM excess_query_results AS eqr
585
+ WHERE
586
+ NOT EXISTS (
587
+ SELECT 1
588
+ FROM excess_query_results AS eqr_errors
589
+ WHERE eqr_errors.error_message IS NOT NULL
590
+ )
591
+ ;
592
+ ```
593
+
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.
@@ -0,0 +1,17 @@
1
+ sqlite_export_for_ynab/__init__.py,sha256=XDobCF9OKc7jzFcC6qVOAeuGFkoCHRSP2sJyA3qZ_R8,178
2
+ sqlite_export_for_ynab/__main__.py,sha256=v66g_E8Y8DwkvguA2qMY9m2LWxMOcbxPF3Q5CEzlf-Y,139
3
+ sqlite_export_for_ynab/_main.py,sha256=SEzLBSuaWuEIpV-rvA6uQ_GL4Dj5UGjIeJth-hapomk,21134
4
+ sqlite_export_for_ynab/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ sqlite_export_for_ynab/ddl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ sqlite_export_for_ynab/ddl/create-relations.sql,sha256=FIBTcIvPezZDb6GgC0aL7uE7MIpLfom5HfNOnk5tA1c,9322
7
+ sqlite_export_for_ynab/ddl/drop-relations.sql,sha256=cOhRhvL55hNqzpbJFOP-p_UWItqjbQvMm9CMooXQ8S8,470
8
+ sqlite_export_for_ynab-2.9.1.dist-info/licenses/LICENSE,sha256=_S9QRg1_pffWsdzXQ9qHYb6GNBsBBxipqD33puzRjX4,1062
9
+ testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ testing/fixtures.py,sha256=1oJUFvbP3qscZKyf1BJpa6NjBYw3GJAK_-tVI-Zf6PE,20913
11
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ tests/_main_test.py,sha256=-UG_nPZU_3ADY0dhd0O8fmbf8333ThH29lXo2ruNm0o,40689
13
+ sqlite_export_for_ynab-2.9.1.dist-info/METADATA,sha256=35VzJEXyOek4vcpuIy7IFPPPnsbrsDi8i6ThxDm_XUo,17457
14
+ sqlite_export_for_ynab-2.9.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
15
+ sqlite_export_for_ynab-2.9.1.dist-info/entry_points.txt,sha256=P587aLBQ4w-2K0r_wE_0dXOcwTY9fQ_udZtD73xza-E,77
16
+ sqlite_export_for_ynab-2.9.1.dist-info/top_level.txt,sha256=pnNK1OqG2G7U9tV7JP78BgV06F7K9iz53T1J4ofiDsw,37
17
+ sqlite_export_for_ynab-2.9.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sqlite-export-for-ynab = sqlite_export_for_ynab._main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Max R
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ sqlite_export_for_ynab
2
+ testing
3
+ tests
testing/__init__.py ADDED
File without changes