sqlite-export-for-ynab 2.9.3__tar.gz → 2.9.4__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.3/sqlite_export_for_ynab.egg-info → sqlite_export_for_ynab-2.9.4}/PKG-INFO +2 -2
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/pyproject.toml +2 -2
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/_main.py +79 -7
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4/sqlite_export_for_ynab.egg-info}/PKG-INFO +2 -2
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab.egg-info/requires.txt +1 -1
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/testing/fixtures.py +7 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/tests/_main_test.py +92 -1
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/LICENSE +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/README.md +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/setup.cfg +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/__main__.py +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/ddl/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/ddl/create-relations.sql +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/ddl/drop-relations.sql +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/py.typed +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab.egg-info/SOURCES.txt +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab.egg-info/dependency_links.txt +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab.egg-info/entry_points.txt +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab.egg-info/top_level.txt +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/testing/__init__.py +0 -0
- {sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.4
|
|
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
|
|
@@ -16,7 +16,7 @@ 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.85.0
|
|
20
20
|
Requires-Dist: fasteners
|
|
21
21
|
Requires-Dist: rich>=10
|
|
22
22
|
Requires-Dist: tenacity
|
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
|
22
22
|
"aiohttp>=3",
|
|
23
23
|
"aiopathlib",
|
|
24
24
|
"aiosqlite",
|
|
25
|
-
"asyncio-for-ynab~=1.
|
|
25
|
+
"asyncio-for-ynab~=1.85.0",
|
|
26
26
|
"fasteners",
|
|
27
27
|
"rich>=10",
|
|
28
28
|
"tenacity",
|
|
@@ -34,7 +34,7 @@ license-files = ["LICENSE"]
|
|
|
34
34
|
name = "sqlite_export_for_ynab"
|
|
35
35
|
readme = "README.md"
|
|
36
36
|
requires-python = ">=3.12"
|
|
37
|
-
version = "2.9.
|
|
37
|
+
version = "2.9.4"
|
|
38
38
|
|
|
39
39
|
[project.scripts]
|
|
40
40
|
sqlite-export-for-ynab = "sqlite_export_for_ynab._main:main"
|
{sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/_main.py
RENAMED
|
@@ -7,6 +7,8 @@ from contextlib import asynccontextmanager
|
|
|
7
7
|
from contextlib import contextmanager
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from dataclasses import fields
|
|
10
|
+
from datetime import date
|
|
11
|
+
from datetime import timedelta
|
|
10
12
|
from importlib import resources
|
|
11
13
|
from importlib.metadata import version
|
|
12
14
|
from itertools import batched
|
|
@@ -35,6 +37,8 @@ from asyncio_for_ynab import ScheduledTransactionDetail
|
|
|
35
37
|
from asyncio_for_ynab import ScheduledTransactionsApi
|
|
36
38
|
from asyncio_for_ynab import TransactionDetail
|
|
37
39
|
from asyncio_for_ynab import TransactionsApi
|
|
40
|
+
from asyncio_for_ynab import TransactionsResponse
|
|
41
|
+
from asyncio_for_ynab import TransactionsResponseData
|
|
38
42
|
from rich.progress import BarColumn
|
|
39
43
|
from rich.progress import Progress
|
|
40
44
|
from rich.progress import TaskID
|
|
@@ -295,9 +299,7 @@ async def sync(
|
|
|
295
299
|
"Plan Data", total=len(plans) * len(_ENDPOINTS)
|
|
296
300
|
)
|
|
297
301
|
|
|
298
|
-
all_data = await _get_all_ynab(
|
|
299
|
-
context, [str(plan.id) for plan in plans], lkos, task
|
|
300
|
-
)
|
|
302
|
+
all_data = await _get_all_ynab(context, plans, lkos, task)
|
|
301
303
|
|
|
302
304
|
_print("Done", quiet=quiet)
|
|
303
305
|
|
|
@@ -635,24 +637,30 @@ async def _get_plan_summaries(api_client: ApiClient) -> list[PlanSummary]:
|
|
|
635
637
|
|
|
636
638
|
|
|
637
639
|
async def _get_all_ynab(
|
|
638
|
-
context: _Context,
|
|
640
|
+
context: _Context, plans: list[PlanSummary], lkos: dict[str, int], task_id: TaskID
|
|
639
641
|
) -> dict[str, _YnabPlanData]:
|
|
640
642
|
return dict(
|
|
641
643
|
await asyncio.gather(
|
|
642
|
-
*(_get_plan_data(context,
|
|
644
|
+
*(_get_plan_data(context, plan, lkos, task_id) for plan in plans)
|
|
643
645
|
)
|
|
644
646
|
)
|
|
645
647
|
|
|
646
648
|
|
|
647
649
|
async def _get_plan_data(
|
|
648
|
-
context: _Context,
|
|
650
|
+
context: _Context, plan: PlanSummary, lkos: dict[str, int], task_id: TaskID
|
|
649
651
|
) -> tuple[str, _YnabPlanData]:
|
|
652
|
+
plan_id = str(plan.id)
|
|
653
|
+
assert plan.first_month is not None
|
|
650
654
|
py = _ProgressYnab(context, plan_id, lkos, task_id)
|
|
651
655
|
accounts, categories, payees, transactions, scheduled = await asyncio.gather(
|
|
652
656
|
py.get(AccountsApi(context.api_client).get_accounts),
|
|
653
657
|
py.get(CategoriesApi(context.api_client).get_categories),
|
|
654
658
|
py.get(PayeesApi(context.api_client).get_payees),
|
|
655
|
-
py.get(
|
|
659
|
+
py.get(
|
|
660
|
+
ChunkedTransactionsApi(
|
|
661
|
+
context.api_client, plan.first_month
|
|
662
|
+
).get_transactions
|
|
663
|
+
),
|
|
656
664
|
py.get(ScheduledTransactionsApi(context.api_client).get_scheduled_transactions),
|
|
657
665
|
)
|
|
658
666
|
return (
|
|
@@ -686,6 +694,70 @@ class _ProgressYnab:
|
|
|
686
694
|
self.context.progress.update(self.task_id, advance=1)
|
|
687
695
|
|
|
688
696
|
|
|
697
|
+
@dataclass(slots=True, frozen=True)
|
|
698
|
+
class ChunkedTransactionsApi:
|
|
699
|
+
api_client: ApiClient
|
|
700
|
+
first_month: date
|
|
701
|
+
|
|
702
|
+
async def get_transactions(
|
|
703
|
+
self, *, plan_id: str, last_knowledge_of_server: int | None
|
|
704
|
+
) -> TransactionsResponse:
|
|
705
|
+
transactions_api = TransactionsApi(self.api_client)
|
|
706
|
+
if last_knowledge_of_server is not None:
|
|
707
|
+
return await transactions_api.get_transactions(
|
|
708
|
+
plan_id=plan_id,
|
|
709
|
+
last_knowledge_of_server=last_knowledge_of_server,
|
|
710
|
+
)
|
|
711
|
+
return await self._get(transactions_api, plan_id)
|
|
712
|
+
|
|
713
|
+
async def _get(
|
|
714
|
+
self, transactions_api: TransactionsApi, plan_id: str
|
|
715
|
+
) -> TransactionsResponse:
|
|
716
|
+
today = date.today()
|
|
717
|
+
responses = await asyncio.gather(
|
|
718
|
+
*(
|
|
719
|
+
self._chunk(transactions_api, plan_id, sd, ud)
|
|
720
|
+
for sd, ud in _quarterly(self.first_month, today)
|
|
721
|
+
)
|
|
722
|
+
)
|
|
723
|
+
transactions = [t for r in responses for t in r.data.transactions]
|
|
724
|
+
ids = [t.id for t in transactions]
|
|
725
|
+
assert len(set(ids)) == len(ids) # paranoid check for no overlap across chunks
|
|
726
|
+
return TransactionsResponse(
|
|
727
|
+
data=TransactionsResponseData(
|
|
728
|
+
transactions=transactions,
|
|
729
|
+
server_knowledge=max(r.data.server_knowledge for r in responses),
|
|
730
|
+
)
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
@retry(stop=stop_after_attempt(3))
|
|
734
|
+
async def _chunk(
|
|
735
|
+
self,
|
|
736
|
+
transactions_api: TransactionsApi,
|
|
737
|
+
plan_id: str,
|
|
738
|
+
since_date: date,
|
|
739
|
+
until_date: date,
|
|
740
|
+
) -> TransactionsResponse:
|
|
741
|
+
return await transactions_api.get_transactions(
|
|
742
|
+
plan_id=plan_id,
|
|
743
|
+
since_date=since_date,
|
|
744
|
+
until_date=until_date,
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
def _add_months(d: date, months: int) -> date:
|
|
749
|
+
month_index = d.month - 1 + months
|
|
750
|
+
return date(d.year + month_index // 12, month_index % 12 + 1, 1)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def _quarterly(first_month: date, today: date) -> Iterator[tuple[date, date]]:
|
|
754
|
+
since_date = first_month
|
|
755
|
+
while since_date <= today:
|
|
756
|
+
until_date = min(_add_months(since_date, 3) - timedelta(days=1), today)
|
|
757
|
+
yield since_date, until_date
|
|
758
|
+
since_date = until_date + timedelta(days=1)
|
|
759
|
+
|
|
760
|
+
|
|
689
761
|
def main(
|
|
690
762
|
argv: Sequence[str] | None = None, *, token_override: str | None = None
|
|
691
763
|
) -> int:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlite_export_for_ynab
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.4
|
|
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
|
|
@@ -16,7 +16,7 @@ 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.85.0
|
|
20
20
|
Requires-Dist: fasteners
|
|
21
21
|
Requires-Dist: rich>=10
|
|
22
22
|
Requires-Dist: tenacity
|
|
@@ -37,6 +37,7 @@ PLANS: list[PlanSummary] = [
|
|
|
37
37
|
PlanSummary(
|
|
38
38
|
id=UUID(PLAN_ID_1),
|
|
39
39
|
name="Plan 1",
|
|
40
|
+
first_month=date.fromisoformat("2024-01-01"),
|
|
40
41
|
currency_format=CurrencyFormat(
|
|
41
42
|
currency_symbol="$",
|
|
42
43
|
decimal_digits=2,
|
|
@@ -51,6 +52,7 @@ PLANS: list[PlanSummary] = [
|
|
|
51
52
|
PlanSummary(
|
|
52
53
|
id=UUID(PLAN_ID_2),
|
|
53
54
|
name="Plan 2",
|
|
55
|
+
first_month=date.fromisoformat("2024-01-01"),
|
|
54
56
|
currency_format=CurrencyFormat(
|
|
55
57
|
currency_symbol="$",
|
|
56
58
|
decimal_digits=2,
|
|
@@ -67,6 +69,11 @@ PLANS: list[PlanSummary] = [
|
|
|
67
69
|
SERVER_KNOWLEDGE_1 = 107667
|
|
68
70
|
SERVER_KNOWLEDGE_2 = 107668
|
|
69
71
|
|
|
72
|
+
TRANSACTION_CHUNKS = [
|
|
73
|
+
(date(2021, 1, 1), date(2021, 3, 31)),
|
|
74
|
+
(date(2021, 4, 1), date(2021, 6, 30)),
|
|
75
|
+
]
|
|
76
|
+
|
|
70
77
|
LKOS = {
|
|
71
78
|
PLAN_ID_1: SERVER_KNOWLEDGE_1,
|
|
72
79
|
PLAN_ID_2: SERVER_KNOWLEDGE_2,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import tomllib
|
|
4
|
+
from datetime import date
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from unittest.mock import AsyncMock
|
|
6
7
|
from unittest.mock import Mock
|
|
@@ -20,8 +21,10 @@ from sqlite_export_for_ynab._main import _ENV_TOKEN
|
|
|
20
21
|
from sqlite_export_for_ynab._main import _get_plan_summaries
|
|
21
22
|
from sqlite_export_for_ynab._main import _PACKAGE
|
|
22
23
|
from sqlite_export_for_ynab._main import _PROGRESS_COLUMNS
|
|
24
|
+
from sqlite_export_for_ynab._main import _quarterly
|
|
23
25
|
from sqlite_export_for_ynab._main import async_main
|
|
24
26
|
from sqlite_export_for_ynab._main import asyncio_for_ynab
|
|
27
|
+
from sqlite_export_for_ynab._main import ChunkedTransactionsApi
|
|
25
28
|
from sqlite_export_for_ynab._main import contents
|
|
26
29
|
from sqlite_export_for_ynab._main import get_last_knowledge_of_server
|
|
27
30
|
from sqlite_export_for_ynab._main import get_relations
|
|
@@ -71,9 +74,11 @@ from testing.fixtures import SCHEDULED_TRANSACTION_ID_3
|
|
|
71
74
|
from testing.fixtures import SCHEDULED_TRANSACTIONS
|
|
72
75
|
from testing.fixtures import scheduled_transactions_response
|
|
73
76
|
from testing.fixtures import SERVER_KNOWLEDGE_1
|
|
77
|
+
from testing.fixtures import SERVER_KNOWLEDGE_2
|
|
74
78
|
from testing.fixtures import SUBTRANSACTION_ID_1
|
|
75
79
|
from testing.fixtures import SUBTRANSACTION_ID_2
|
|
76
80
|
from testing.fixtures import TOKEN
|
|
81
|
+
from testing.fixtures import TRANSACTION_CHUNKS
|
|
77
82
|
from testing.fixtures import TRANSACTION_ID_1
|
|
78
83
|
from testing.fixtures import TRANSACTION_ID_2
|
|
79
84
|
from testing.fixtures import TRANSACTION_ID_3
|
|
@@ -106,6 +111,87 @@ async def context(tmp_path):
|
|
|
106
111
|
yield _Context(progress, con, lock, Mock(spec=asyncio_for_ynab.ApiClient))
|
|
107
112
|
|
|
108
113
|
|
|
114
|
+
@patch("sqlite_export_for_ynab._main.TransactionsApi.get_transactions")
|
|
115
|
+
@pytest.mark.asyncio
|
|
116
|
+
async def test_chunked_transactions_api_uses_last_knowledge_of_server_when_present(
|
|
117
|
+
get_transactions, context
|
|
118
|
+
):
|
|
119
|
+
get_transactions.return_value = transactions_response(
|
|
120
|
+
TRANSACTIONS, SERVER_KNOWLEDGE_1
|
|
121
|
+
)
|
|
122
|
+
chunked_transactions_api = ChunkedTransactionsApi(
|
|
123
|
+
context.api_client, date(2020, 1, 1)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
response = await chunked_transactions_api.get_transactions(
|
|
127
|
+
plan_id=PLAN_ID_1, last_knowledge_of_server=LKOS[PLAN_ID_1]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
get_transactions.assert_awaited_once_with(
|
|
131
|
+
plan_id=PLAN_ID_1, last_knowledge_of_server=LKOS[PLAN_ID_1]
|
|
132
|
+
)
|
|
133
|
+
assert response.data.transactions == TRANSACTIONS
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.parametrize(
|
|
137
|
+
("first_month", "today", "expected_chunks"),
|
|
138
|
+
(
|
|
139
|
+
pytest.param(
|
|
140
|
+
date(2021, 4, 1),
|
|
141
|
+
date(2022, 1, 15),
|
|
142
|
+
[
|
|
143
|
+
(date(2021, 4, 1), date(2021, 6, 30)),
|
|
144
|
+
(date(2021, 7, 1), date(2021, 9, 30)),
|
|
145
|
+
(date(2021, 10, 1), date(2021, 12, 31)),
|
|
146
|
+
(date(2022, 1, 1), date(2022, 1, 15)),
|
|
147
|
+
],
|
|
148
|
+
id="spans-multiple-quarters-with-partial-last-chunk",
|
|
149
|
+
),
|
|
150
|
+
pytest.param(
|
|
151
|
+
date(2024, 1, 1),
|
|
152
|
+
date(2024, 1, 1),
|
|
153
|
+
[(date(2024, 1, 1), date(2024, 1, 1))],
|
|
154
|
+
id="single-day-range",
|
|
155
|
+
),
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
def test_quarterly(first_month, today, expected_chunks):
|
|
159
|
+
assert list(_quarterly(first_month, today)) == expected_chunks
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _chunk_merge_test_side_effect(*, plan_id, since_date, until_date):
|
|
163
|
+
assert plan_id == PLAN_ID_1
|
|
164
|
+
assert (since_date, until_date) in TRANSACTION_CHUNKS
|
|
165
|
+
old_transaction, new_transaction, _ = TRANSACTIONS
|
|
166
|
+
if since_date == TRANSACTION_CHUNKS[0][0]:
|
|
167
|
+
return transactions_response([old_transaction], SERVER_KNOWLEDGE_1)
|
|
168
|
+
return transactions_response([new_transaction], SERVER_KNOWLEDGE_2)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@patch("sqlite_export_for_ynab._main._quarterly")
|
|
172
|
+
@patch("sqlite_export_for_ynab._main.TransactionsApi.get_transactions")
|
|
173
|
+
@pytest.mark.asyncio
|
|
174
|
+
async def test_chunked_transactions_api_merges_chunk_responses(
|
|
175
|
+
get_transactions, quarterly_chunks, context
|
|
176
|
+
):
|
|
177
|
+
get_transactions.side_effect = _chunk_merge_test_side_effect
|
|
178
|
+
quarterly_chunks.return_value = iter(TRANSACTION_CHUNKS)
|
|
179
|
+
chunked_transactions_api = ChunkedTransactionsApi(
|
|
180
|
+
context.api_client, date(2021, 1, 1)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
response = await chunked_transactions_api.get_transactions(
|
|
184
|
+
plan_id=PLAN_ID_1, last_knowledge_of_server=None
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
old_transaction, new_transaction, _ = TRANSACTIONS
|
|
188
|
+
assert {t.id for t in response.data.transactions} == {
|
|
189
|
+
old_transaction.id,
|
|
190
|
+
new_transaction.id,
|
|
191
|
+
}
|
|
192
|
+
assert response.data.server_knowledge == SERVER_KNOWLEDGE_2
|
|
193
|
+
|
|
194
|
+
|
|
109
195
|
@pytest.mark.parametrize(
|
|
110
196
|
("xdg_data_home", "expected_prefix"),
|
|
111
197
|
(
|
|
@@ -1109,7 +1195,12 @@ async def test_sync_no_data_quiet(tmp_path, capsys):
|
|
|
1109
1195
|
)
|
|
1110
1196
|
@patch(
|
|
1111
1197
|
"sqlite_export_for_ynab._main.TransactionsApi.get_transactions",
|
|
1112
|
-
new=AsyncMock(
|
|
1198
|
+
new=AsyncMock(
|
|
1199
|
+
side_effect=lambda *, plan_id, since_date, until_date: transactions_response(
|
|
1200
|
+
[t for t in TRANSACTIONS if since_date <= t.var_date <= until_date],
|
|
1201
|
+
SERVER_KNOWLEDGE_1,
|
|
1202
|
+
)
|
|
1203
|
+
),
|
|
1113
1204
|
)
|
|
1114
1205
|
@patch(
|
|
1115
1206
|
"sqlite_export_for_ynab._main.ScheduledTransactionsApi.get_scheduled_transactions",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/__init__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/__main__.py
RENAMED
|
File without changes
|
{sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/sqlite_export_for_ynab/ddl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlite_export_for_ynab-2.9.3 → sqlite_export_for_ynab-2.9.4}/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
|