lunchmoney-python-async 2.10.0__py3-none-any.whl → 2.18.2__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.
- lunchmoney/__init__.py +1 -1
- lunchmoney/api_client.py +1 -1
- lunchmoney/app.py +589 -0
- lunchmoney/configuration.py +1 -1
- {lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/METADATA +2 -2
- {lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/RECORD +8 -7
- {lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/WHEEL +1 -1
- {lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/top_level.txt +0 -0
lunchmoney/__init__.py
CHANGED
lunchmoney/api_client.py
CHANGED
|
@@ -92,7 +92,7 @@ class ApiClient:
|
|
|
92
92
|
self.default_headers[header_name] = header_value
|
|
93
93
|
self.cookie = cookie
|
|
94
94
|
# Set default User-Agent.
|
|
95
|
-
self.user_agent = 'OpenAPI-Generator/2.
|
|
95
|
+
self.user_agent = 'OpenAPI-Generator/2.18.2/python'
|
|
96
96
|
self.client_side_validation = configuration.client_side_validation
|
|
97
97
|
|
|
98
98
|
async def __aenter__(self):
|
lunchmoney/app.py
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Classes for LunchMoneyApp
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import datetime
|
|
7
|
+
import logging
|
|
8
|
+
from collections.abc import AsyncIterable, Iterable
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from os import getenv
|
|
12
|
+
from typing import Any, Callable, ClassVar, NamedTuple, TypeVar, overload
|
|
13
|
+
|
|
14
|
+
import lunchmoney
|
|
15
|
+
from lunchmoney import GetAllTransactions200Response
|
|
16
|
+
from lunchmoney.models import ( # type: ignore[attr-defined]
|
|
17
|
+
CategoryObject,
|
|
18
|
+
ManualAccountObject,
|
|
19
|
+
PlaidAccountObject,
|
|
20
|
+
TagObject,
|
|
21
|
+
TransactionObject,
|
|
22
|
+
UserObject,
|
|
23
|
+
)
|
|
24
|
+
from pydantic import BaseModel
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
LunchableModelType = TypeVar("LunchableModelType", bound=BaseModel)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(slots=True)
|
|
33
|
+
class LunchableData:
|
|
34
|
+
"""
|
|
35
|
+
Data Container for Lunchable App Data
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
plaid_accounts: dict[int, PlaidAccountObject] = field(default_factory=dict)
|
|
39
|
+
"""Plaid Accounts"""
|
|
40
|
+
transactions: dict[int, TransactionObject] = field(default_factory=dict)
|
|
41
|
+
"""Transactions"""
|
|
42
|
+
categories: dict[int, CategoryObject] = field(default_factory=dict)
|
|
43
|
+
"""Categories"""
|
|
44
|
+
manual_accounts: dict[int, ManualAccountObject] = field(default_factory=dict)
|
|
45
|
+
"""Manual Accounts"""
|
|
46
|
+
tags: dict[int, TagObject] = field(default_factory=dict)
|
|
47
|
+
"""Tags"""
|
|
48
|
+
user: UserObject | None = None
|
|
49
|
+
"""User"""
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def current_user(self) -> UserObject:
|
|
53
|
+
"""
|
|
54
|
+
Current User Object
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
UserObject
|
|
59
|
+
"""
|
|
60
|
+
if not self.user:
|
|
61
|
+
raise ValueError("User data has not been loaded.")
|
|
62
|
+
return self.user
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def asset_map(self) -> dict[int, PlaidAccountObject | ManualAccountObject]:
|
|
66
|
+
"""
|
|
67
|
+
Asset Mapping Across Plaid Accounts and Assets
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
dict[int, Union[PlaidAccountObject, ManualAccountObject]]
|
|
72
|
+
"""
|
|
73
|
+
return {
|
|
74
|
+
**self.plaid_accounts,
|
|
75
|
+
**self.manual_accounts,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def plaid_accounts_list(self) -> list[PlaidAccountObject]:
|
|
80
|
+
"""
|
|
81
|
+
List of Plaid Accounts
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
list[PlaidAccountObject]
|
|
86
|
+
"""
|
|
87
|
+
return list(self.plaid_accounts.values())
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def manual_accounts_list(self) -> list[ManualAccountObject]:
|
|
91
|
+
"""
|
|
92
|
+
List of Manual Accounts
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
list[ManualAccountObject]
|
|
97
|
+
"""
|
|
98
|
+
return list(self.manual_accounts.values())
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def transactions_list(self) -> list[TransactionObject]:
|
|
102
|
+
"""
|
|
103
|
+
List of Transactions
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
list[TransactionObject]
|
|
108
|
+
"""
|
|
109
|
+
return list(self.transactions.values())
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def categories_list(self) -> list[CategoryObject]:
|
|
113
|
+
"""
|
|
114
|
+
List of Categories
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
list[CategoryObject]
|
|
119
|
+
"""
|
|
120
|
+
return list(self.categories.values())
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def tags_list(self) -> list[TagObject]:
|
|
124
|
+
"""
|
|
125
|
+
List of Tags
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
list[TagObject]
|
|
130
|
+
"""
|
|
131
|
+
return list(self.tags.values())
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class _ObjectMapper(NamedTuple):
|
|
135
|
+
"""
|
|
136
|
+
Object Mapper for Lunchable Models
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
func: Callable[..., Any]
|
|
140
|
+
data_attr: str
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(slots=True)
|
|
144
|
+
class LunchableApi:
|
|
145
|
+
"""
|
|
146
|
+
API Container for Lunchable App APIs
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
plaid: lunchmoney.PlaidAccountsApi
|
|
150
|
+
transactions: lunchmoney.TransactionsApi
|
|
151
|
+
transactions_bulk: lunchmoney.TransactionsBulkApi
|
|
152
|
+
categories: lunchmoney.CategoriesApi
|
|
153
|
+
manual_accounts: lunchmoney.ManualAccountsApi
|
|
154
|
+
tags: lunchmoney.TagsApi
|
|
155
|
+
me: lunchmoney.MeApi
|
|
156
|
+
recurring_items: lunchmoney.RecurringItemsApi
|
|
157
|
+
summary: lunchmoney.SummaryApi
|
|
158
|
+
transactions_files: lunchmoney.TransactionsFilesApi
|
|
159
|
+
transactions_group: lunchmoney.TransactionsGroupApi
|
|
160
|
+
transactions_split: lunchmoney.TransactionsSplitApi
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
def from_client(cls, client: lunchmoney.ApiClient) -> "LunchableApi":
|
|
164
|
+
"""
|
|
165
|
+
Initialize LunchableApi from ApiClient
|
|
166
|
+
"""
|
|
167
|
+
return cls(
|
|
168
|
+
plaid=lunchmoney.PlaidAccountsApi(client),
|
|
169
|
+
transactions=lunchmoney.TransactionsApi(client),
|
|
170
|
+
transactions_bulk=lunchmoney.TransactionsBulkApi(client),
|
|
171
|
+
categories=lunchmoney.CategoriesApi(client),
|
|
172
|
+
manual_accounts=lunchmoney.ManualAccountsApi(client),
|
|
173
|
+
tags=lunchmoney.TagsApi(client),
|
|
174
|
+
me=lunchmoney.MeApi(client),
|
|
175
|
+
recurring_items=lunchmoney.RecurringItemsApi(client),
|
|
176
|
+
summary=lunchmoney.SummaryApi(client),
|
|
177
|
+
transactions_files=lunchmoney.TransactionsFilesApi(client),
|
|
178
|
+
transactions_group=lunchmoney.TransactionsGroupApi(client),
|
|
179
|
+
transactions_split=lunchmoney.TransactionsSplitApi(client),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LunchMoneyApp:
|
|
184
|
+
"""
|
|
185
|
+
Base LunchMoney App Class
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
lunchable_models: ClassVar[Iterable[type[BaseModel]]] = [
|
|
189
|
+
PlaidAccountObject,
|
|
190
|
+
CategoryObject,
|
|
191
|
+
ManualAccountObject,
|
|
192
|
+
TagObject,
|
|
193
|
+
UserObject,
|
|
194
|
+
]
|
|
195
|
+
"""Every LunchableApp should define which data objects it depends on"""
|
|
196
|
+
lunchable_models_kwargs: ClassVar[dict[type[BaseModel], dict[str, Any]]] = {}
|
|
197
|
+
"""Optional keyword arguments to pass to model constructors (supports callables)"""
|
|
198
|
+
transaction_pagination: ClassVar[int] = 500
|
|
199
|
+
"""Number of Transactions to fetch per page during pagination"""
|
|
200
|
+
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
access_token: str | None = None,
|
|
204
|
+
lunchable_models: Iterable[type[BaseModel]] | None = None,
|
|
205
|
+
lunchable_models_kwargs: dict[type[BaseModel], dict[str, Any]] | None = None,
|
|
206
|
+
transaction_pagination: int | None = None,
|
|
207
|
+
) -> None:
|
|
208
|
+
"""
|
|
209
|
+
Initialize LunchMoneyApp
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
access_token: str | None
|
|
214
|
+
LunchMoney Access Token. If not provided, will attempt to read from
|
|
215
|
+
`LUNCHMONEY_ACCESS_TOKEN` environment variable.
|
|
216
|
+
lunchable_models: Iterable[type[LunchableModelType]] | None
|
|
217
|
+
Explicit list of Lunchable Models to use in this app. If not provided,
|
|
218
|
+
will default to the class variable `lunchable_models`.
|
|
219
|
+
lunchable_models_kwargs: dict[type[LunchableModelType], dict[str, Any]] | None
|
|
220
|
+
Optional keyword arguments to pass to model constructors. If not provided,
|
|
221
|
+
will default to the class variable `lunchable_models_kwargs`.
|
|
222
|
+
transaction_pagination: int | None
|
|
223
|
+
Number of Transactions to fetch per page during pagination. If not provided,
|
|
224
|
+
will default to the class variable `transaction_pagination`.
|
|
225
|
+
"""
|
|
226
|
+
access_token = access_token or getenv("LUNCHMONEY_ACCESS_TOKEN")
|
|
227
|
+
if not access_token:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
"LunchMoney API key must be provided via "
|
|
230
|
+
"parameter or LUNCHMONEY_ACCESS_TOKEN environment "
|
|
231
|
+
"variable."
|
|
232
|
+
)
|
|
233
|
+
configuration = lunchmoney.Configuration(
|
|
234
|
+
host="https://api.lunchmoney.dev/v2", access_token=access_token
|
|
235
|
+
)
|
|
236
|
+
self.client: lunchmoney.ApiClient = lunchmoney.ApiClient(
|
|
237
|
+
configuration=configuration
|
|
238
|
+
)
|
|
239
|
+
self.api: LunchableApi = LunchableApi.from_client(self.client)
|
|
240
|
+
self.data: LunchableData = LunchableData()
|
|
241
|
+
self._lunchable_models: Iterable[type[BaseModel]] = (
|
|
242
|
+
lunchable_models or self.__class__.lunchable_models
|
|
243
|
+
)
|
|
244
|
+
self._lunchable_models_kwargs: dict[type[BaseModel], dict[str, Any]] = (
|
|
245
|
+
lunchable_models_kwargs or self.__class__.lunchable_models_kwargs
|
|
246
|
+
)
|
|
247
|
+
self._transaction_pagination: int = (
|
|
248
|
+
transaction_pagination or self.__class__.transaction_pagination
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
@cached_property
|
|
252
|
+
def _model_mapping(self) -> dict[type[BaseModel], _ObjectMapper]:
|
|
253
|
+
"""
|
|
254
|
+
Model Class -> _ObjectMapper Mapping
|
|
255
|
+
"""
|
|
256
|
+
return {
|
|
257
|
+
PlaidAccountObject: _ObjectMapper(
|
|
258
|
+
func=self.api.plaid.get_all_plaid_accounts,
|
|
259
|
+
data_attr="plaid_accounts",
|
|
260
|
+
),
|
|
261
|
+
TransactionObject: _ObjectMapper(
|
|
262
|
+
func=self.api.transactions_bulk.get_all_transactions,
|
|
263
|
+
data_attr="transactions",
|
|
264
|
+
),
|
|
265
|
+
CategoryObject: _ObjectMapper(
|
|
266
|
+
func=self.api.categories.get_all_categories,
|
|
267
|
+
data_attr="categories",
|
|
268
|
+
),
|
|
269
|
+
ManualAccountObject: _ObjectMapper(
|
|
270
|
+
func=self.api.manual_accounts.get_all_manual_accounts,
|
|
271
|
+
data_attr="manual_accounts",
|
|
272
|
+
),
|
|
273
|
+
TagObject: _ObjectMapper(
|
|
274
|
+
func=self.api.tags.get_all_tags,
|
|
275
|
+
data_attr="tags",
|
|
276
|
+
),
|
|
277
|
+
UserObject: _ObjectMapper(
|
|
278
|
+
func=self.api.me.get_me,
|
|
279
|
+
data_attr="me",
|
|
280
|
+
),
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
def _get_model_mapper(self, model: type[LunchableModelType]) -> _ObjectMapper:
|
|
284
|
+
"""
|
|
285
|
+
Get the appropriate function for a given Lunchable Model
|
|
286
|
+
|
|
287
|
+
Parameters
|
|
288
|
+
----------
|
|
289
|
+
model: Type[LunchableModelType]
|
|
290
|
+
Type of Lunchable Model to get the function for
|
|
291
|
+
|
|
292
|
+
Returns
|
|
293
|
+
-------
|
|
294
|
+
_ObjectMapper
|
|
295
|
+
_ObjectMapper containing the function and data attribute name
|
|
296
|
+
"""
|
|
297
|
+
mapper = self._model_mapping.get(model)
|
|
298
|
+
if not mapper:
|
|
299
|
+
msg = f"Model {model.__name__} is not supported for refresh."
|
|
300
|
+
raise NotImplementedError(msg)
|
|
301
|
+
return mapper
|
|
302
|
+
|
|
303
|
+
def _resolve_model_kwargs(
|
|
304
|
+
self, model: type[LunchableModelType], **kwargs: Any
|
|
305
|
+
) -> dict[str, Any]:
|
|
306
|
+
"""
|
|
307
|
+
Resolve Any Model Kwargs that Are Callable
|
|
308
|
+
"""
|
|
309
|
+
model_kwargs = self._lunchable_models_kwargs.get(model, {}).copy()
|
|
310
|
+
model_kwargs.update(kwargs)
|
|
311
|
+
resolved_kwargs: dict[str, Any] = {}
|
|
312
|
+
for key, value in model_kwargs.items():
|
|
313
|
+
if callable(value):
|
|
314
|
+
resolved_value = value()
|
|
315
|
+
else:
|
|
316
|
+
resolved_value = value
|
|
317
|
+
resolved_kwargs[key] = resolved_value
|
|
318
|
+
return resolved_kwargs
|
|
319
|
+
|
|
320
|
+
@overload
|
|
321
|
+
async def refresh(self, model: type[UserObject], **kwargs: Any) -> UserObject: ...
|
|
322
|
+
|
|
323
|
+
@overload
|
|
324
|
+
async def refresh(
|
|
325
|
+
self, model: type[TransactionObject], **kwargs: Any
|
|
326
|
+
) -> dict[int, TransactionObject]: ...
|
|
327
|
+
|
|
328
|
+
@overload
|
|
329
|
+
async def refresh(
|
|
330
|
+
self, model: type[LunchableModelType], **kwargs: Any
|
|
331
|
+
) -> dict[int, LunchableModelType]: ...
|
|
332
|
+
|
|
333
|
+
async def refresh(
|
|
334
|
+
self, model: type[LunchableModelType], **kwargs: Any
|
|
335
|
+
) -> LunchableModelType | dict[int, LunchableModelType]:
|
|
336
|
+
"""
|
|
337
|
+
Refresh a Lunchable Model
|
|
338
|
+
|
|
339
|
+
Parameters
|
|
340
|
+
----------
|
|
341
|
+
model: Type[LunchableModelType]
|
|
342
|
+
Type of Lunchable Model to refresh
|
|
343
|
+
kwargs: Any
|
|
344
|
+
Additional keyword arguments to pass to the function that
|
|
345
|
+
fetches the data.
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
LunchableModelType | dict[int, LunchableModelType]
|
|
350
|
+
Unless you're requesting the `UserObject`, this method will return a
|
|
351
|
+
dictionary of the refreshed data, keyed by the object's ID.
|
|
352
|
+
|
|
353
|
+
Examples
|
|
354
|
+
--------
|
|
355
|
+
```python
|
|
356
|
+
from lunchable.models import CategoriesObject
|
|
357
|
+
from lunchable.plugins import LunchableApp
|
|
358
|
+
|
|
359
|
+
app = LunchableApp()
|
|
360
|
+
categories: dict[int, CategoriesObject] = app.refresh(CategoriesObject)
|
|
361
|
+
```
|
|
362
|
+
"""
|
|
363
|
+
mapper = self._get_model_mapper(model)
|
|
364
|
+
model_kwargs = self._resolve_model_kwargs(model=model, **kwargs)
|
|
365
|
+
logger.info("Refreshing LunchMoney Data: %s", mapper.data_attr)
|
|
366
|
+
if model is UserObject:
|
|
367
|
+
user = await mapper.func(**model_kwargs)
|
|
368
|
+
self.data.user = user
|
|
369
|
+
return user
|
|
370
|
+
elif model is TransactionObject:
|
|
371
|
+
transaction_map: dict[int, TransactionObject] = {
|
|
372
|
+
obj.id: obj async for obj in self._paginate_transactions(**model_kwargs)
|
|
373
|
+
}
|
|
374
|
+
logger.info("Refreshed LunchMoney Transactions (%s)", len(transaction_map))
|
|
375
|
+
self.data.transactions.update(transaction_map)
|
|
376
|
+
return transaction_map # type: ignore[return-value]
|
|
377
|
+
else:
|
|
378
|
+
response = await mapper.func(**model_kwargs)
|
|
379
|
+
data_dict = {obj.id: obj for obj in getattr(response, mapper.data_attr)}
|
|
380
|
+
setattr(self.data, mapper.data_attr, data_dict)
|
|
381
|
+
return getattr(self.data, mapper.data_attr)
|
|
382
|
+
|
|
383
|
+
async def refresh_data(
|
|
384
|
+
self, models: Iterable[type[LunchableModelType]] | None = None
|
|
385
|
+
) -> None:
|
|
386
|
+
"""
|
|
387
|
+
Refresh the data in the Lunchable App
|
|
388
|
+
|
|
389
|
+
Parameters
|
|
390
|
+
----------
|
|
391
|
+
models: Iterable[type[LunchableModelType]] | None
|
|
392
|
+
Explicit list of Lunchable Models to refresh. If not provided,
|
|
393
|
+
all models defined in will be refreshed (which by default is
|
|
394
|
+
all of them except for transactions)
|
|
395
|
+
|
|
396
|
+
Examples
|
|
397
|
+
--------
|
|
398
|
+
```python
|
|
399
|
+
from lunchable.models import PlaidAccountObject
|
|
400
|
+
from lunchable.plugins import LunchableApp
|
|
401
|
+
|
|
402
|
+
app = LunchableApp()
|
|
403
|
+
app.refresh_data()
|
|
404
|
+
plaid_accounts: dict[int, PlaidAccountObject] = app.data.plaid_accounts
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
from lunchable.models import AssetsObject
|
|
409
|
+
from lunchable.plugins import LunchableApp
|
|
410
|
+
|
|
411
|
+
app = LunchableApp()
|
|
412
|
+
app.refresh_data(models=[AssetsObject])
|
|
413
|
+
assets: dict[int, AssetsObject] = app.data.assets
|
|
414
|
+
```
|
|
415
|
+
"""
|
|
416
|
+
refresh_models = models or self._lunchable_models
|
|
417
|
+
await asyncio.gather(*[self.refresh(model) for model in set(refresh_models)])
|
|
418
|
+
|
|
419
|
+
async def refresh_transactions(
|
|
420
|
+
self,
|
|
421
|
+
start_date: datetime.date | None = None,
|
|
422
|
+
end_date: datetime.date | None = None,
|
|
423
|
+
created_since: datetime.date | str | None = None,
|
|
424
|
+
updated_since: datetime.date | str | None = None,
|
|
425
|
+
manual_account_id: int | None = None,
|
|
426
|
+
plaid_account_id: int | None = None,
|
|
427
|
+
recurring_id: int | None = None,
|
|
428
|
+
category_id: int | None = None,
|
|
429
|
+
tag_id: int | None = None,
|
|
430
|
+
is_group_parent: bool | None = None,
|
|
431
|
+
status: str | None = None,
|
|
432
|
+
is_pending: bool | None = None,
|
|
433
|
+
include_pending: bool | None = None,
|
|
434
|
+
include_metadata: bool | None = None,
|
|
435
|
+
include_split_parents: bool | None = None,
|
|
436
|
+
include_group_children: bool | None = None,
|
|
437
|
+
include_files: bool | None = None,
|
|
438
|
+
) -> dict[int, TransactionObject]:
|
|
439
|
+
"""
|
|
440
|
+
Refresh Transactions in the App
|
|
441
|
+
|
|
442
|
+
Parameters
|
|
443
|
+
----------
|
|
444
|
+
start_date: datetime.date | None
|
|
445
|
+
Denotes the beginning of the time period to fetch transactions for. If
|
|
446
|
+
omitted, the most recent transactions will be returned. See `limit`.
|
|
447
|
+
Required if end_date exists.
|
|
448
|
+
end_date: datetime.date | None
|
|
449
|
+
Denotes the end of the time period you'd like to get transactions for.
|
|
450
|
+
Required if start_date exists.
|
|
451
|
+
created_since: datetime.date | str | None
|
|
452
|
+
Filter transactions to those created after the specified timestamp.
|
|
453
|
+
Accepts either a date (YYYY-MM-DD) or ISO 8601 datetime string.
|
|
454
|
+
Date-only values are interpreted as midnight UTC (00:00:00Z).
|
|
455
|
+
updated_since: datetime.date | str | None
|
|
456
|
+
Filter transactions to those updated after the specified timestamp.
|
|
457
|
+
Accepts either a date (YYYY-MM-DD) or ISO 8601 datetime string.
|
|
458
|
+
Date-only values are interpreted as midnight UTC (00:00:00Z).
|
|
459
|
+
manual_account_id: int | None
|
|
460
|
+
Filter transactions to those associated with specified manual account ID
|
|
461
|
+
or set this to 0 to omit any transactions from manual accounts. Setting
|
|
462
|
+
both this and `plaid_account_id` to 0 will return transactions with no
|
|
463
|
+
account. These are listed as "Cash Transactions" in the Lunch Money GUI.
|
|
464
|
+
Note that transaction groups are not associated with any account. If you
|
|
465
|
+
want the response to include transactions from transaction groups, set
|
|
466
|
+
the `include_group_children` query parameter to `true` when filtering
|
|
467
|
+
by manual accounts.
|
|
468
|
+
plaid_account_id: int | None
|
|
469
|
+
Filter transactions to those associated with specified plaid account ID
|
|
470
|
+
or set this to 0 to omit any transactions from plaid accounts. Setting
|
|
471
|
+
both this and `manual_account_id` to 0 will return transactions with no
|
|
472
|
+
account. These are listed as "Cash Transactions" in the Lunch Money GUI.
|
|
473
|
+
Note that transaction groups are not associated with any account. If you
|
|
474
|
+
want the response to include transactions from transaction groups, set
|
|
475
|
+
the `include_group_children` query parameter to `true` when filtering
|
|
476
|
+
by plaid accounts.
|
|
477
|
+
recurring_id: int | None
|
|
478
|
+
Filter transactions to those associated with specified Recurring Item ID
|
|
479
|
+
category_id: int | None
|
|
480
|
+
Filter transactions to those associated with the specified category ID.
|
|
481
|
+
Will also match category groups. Set this to 0 to return only
|
|
482
|
+
un-categorized transactions.
|
|
483
|
+
tag_id: int | None
|
|
484
|
+
Filter transactions to those that have a tag with the specified Tag ID.
|
|
485
|
+
is_group_parent: bool | None
|
|
486
|
+
Filter by group (returns only transaction groups if `true`).
|
|
487
|
+
status: str | None
|
|
488
|
+
Filter transactions to those with the specified status:
|
|
489
|
+
- `reviewed`: Only user reviewed transactions or those that were
|
|
490
|
+
automatically marked as reviewed due to reviewed recurring_item logic
|
|
491
|
+
- `unreviewed`: Only transactions that need to be reviewed
|
|
492
|
+
- `delete_pending`: Only transactions that require manual intervention
|
|
493
|
+
because the plaid account deleted this transaction after it was
|
|
494
|
+
updated by the user.
|
|
495
|
+
is_pending: bool | None
|
|
496
|
+
Filter transactions by pending status. Set to `true` to return only
|
|
497
|
+
pending transactions, or `false` to return only non-pending
|
|
498
|
+
transactions. When this parameter is set, it takes precedence over
|
|
499
|
+
`include_pending`. Note: Pending transactions always have a status of
|
|
500
|
+
`unreviewed`, so when setting this parameter to `true`, either omit the
|
|
501
|
+
`status` parameter or set it to `unreviewed`.
|
|
502
|
+
include_pending: bool | None
|
|
503
|
+
By default, pending transactions are excluded from results. Set to
|
|
504
|
+
`true` to include imported transactions with a pending status in the
|
|
505
|
+
results. This query param is ignored if the `is_pending` query param is
|
|
506
|
+
also set.
|
|
507
|
+
include_metadata: bool | None
|
|
508
|
+
By default, custom and plaid metadata are not included in the response.
|
|
509
|
+
Set to true if you'd like the returned transactions objects to include
|
|
510
|
+
any metadata associated with the transactions.
|
|
511
|
+
include_split_parents: bool | None
|
|
512
|
+
By default, transactions that were split into multiple transactions are
|
|
513
|
+
not included in the response. Set to true if you'd like the returned
|
|
514
|
+
transactions objects to include any transactions that were split into
|
|
515
|
+
multiple transactions. Use with caution as this data is normally not
|
|
516
|
+
exposed after the split transactions are created.
|
|
517
|
+
include_group_children: bool | None
|
|
518
|
+
By default, individual transactions that joined into a transaction group
|
|
519
|
+
are not included in the response. Set to true if you'd like the returned
|
|
520
|
+
transactions objects to include any transactions that joined into a
|
|
521
|
+
transaction group.
|
|
522
|
+
include_files: bool | None
|
|
523
|
+
By default, the `files` property is not included in the response. Set to
|
|
524
|
+
true if you'd like the responses to include a list of objects that
|
|
525
|
+
describe any files attached to the transactions.
|
|
526
|
+
|
|
527
|
+
Returns
|
|
528
|
+
-------
|
|
529
|
+
dict[int, TransactionObject]
|
|
530
|
+
Dictionary of Transactions keyed by Transaction ID
|
|
531
|
+
"""
|
|
532
|
+
kwargs: dict[str, Any] = {
|
|
533
|
+
"start_date": start_date,
|
|
534
|
+
"end_date": end_date,
|
|
535
|
+
"created_since": created_since,
|
|
536
|
+
"updated_since": updated_since,
|
|
537
|
+
"manual_account_id": manual_account_id,
|
|
538
|
+
"plaid_account_id": plaid_account_id,
|
|
539
|
+
"recurring_id": recurring_id,
|
|
540
|
+
"category_id": category_id,
|
|
541
|
+
"tag_id": tag_id,
|
|
542
|
+
"is_group_parent": is_group_parent,
|
|
543
|
+
"status": status,
|
|
544
|
+
"is_pending": is_pending,
|
|
545
|
+
"include_pending": include_pending,
|
|
546
|
+
"include_metadata": include_metadata,
|
|
547
|
+
"include_split_parents": include_split_parents,
|
|
548
|
+
"include_group_children": include_group_children,
|
|
549
|
+
"include_files": include_files,
|
|
550
|
+
}
|
|
551
|
+
filtered_kwargs: dict[str, Any] = {
|
|
552
|
+
k: v for k, v in kwargs.items() if v is not None
|
|
553
|
+
}
|
|
554
|
+
transaction_map: dict[int, TransactionObject] = {
|
|
555
|
+
obj.id: obj async for obj in self._paginate_transactions(**filtered_kwargs)
|
|
556
|
+
}
|
|
557
|
+
logger.info("Refreshed LunchMoney Transactions (%s)", len(transaction_map))
|
|
558
|
+
self.data.transactions.update(transaction_map)
|
|
559
|
+
return transaction_map
|
|
560
|
+
|
|
561
|
+
async def _paginate_transactions(
|
|
562
|
+
self, **kwargs: Any
|
|
563
|
+
) -> AsyncIterable[TransactionObject]:
|
|
564
|
+
"""
|
|
565
|
+
Paginate Transactions from the App
|
|
566
|
+
"""
|
|
567
|
+
offset = 0
|
|
568
|
+
while True:
|
|
569
|
+
paginated_kwargs = {
|
|
570
|
+
**kwargs,
|
|
571
|
+
"offset": offset,
|
|
572
|
+
"limit": self._transaction_pagination,
|
|
573
|
+
}
|
|
574
|
+
response: GetAllTransactions200Response = (
|
|
575
|
+
await self.api.transactions_bulk.get_all_transactions(
|
|
576
|
+
**paginated_kwargs
|
|
577
|
+
)
|
|
578
|
+
)
|
|
579
|
+
for transaction in response.transactions:
|
|
580
|
+
yield transaction
|
|
581
|
+
if not response.has_more:
|
|
582
|
+
break
|
|
583
|
+
offset += self._transaction_pagination
|
|
584
|
+
|
|
585
|
+
def clear_transactions(self) -> None:
|
|
586
|
+
"""
|
|
587
|
+
Clear Transactions from the App
|
|
588
|
+
"""
|
|
589
|
+
self.data.transactions.clear()
|
lunchmoney/configuration.py
CHANGED
|
@@ -541,7 +541,7 @@ conf = lunchmoney.Configuration(
|
|
|
541
541
|
"OS: {env}\n"\
|
|
542
542
|
"Python Version: {pyversion}\n"\
|
|
543
543
|
"Version of the API: 2.8.4\n"\
|
|
544
|
-
"SDK Package Version: 2.
|
|
544
|
+
"SDK Package Version: 2.18.2".\
|
|
545
545
|
format(env=sys.platform, pyversion=sys.version)
|
|
546
546
|
|
|
547
547
|
def get_host_settings(self) -> List[HostSetting]:
|
{lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lunchmoney-python-async
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.18.2
|
|
4
4
|
Summary: Lunch Money API - v2
|
|
5
5
|
Home-page:
|
|
6
6
|
Author: OpenAPI Generator community
|
|
@@ -57,7 +57,7 @@ If you have been providing feedback on the API during our iterative design proce
|
|
|
57
57
|
This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
|
58
58
|
|
|
59
59
|
- API version: 2.8.4
|
|
60
|
-
- Package version: 2.
|
|
60
|
+
- Package version: 2.18.2
|
|
61
61
|
- Generator version: 7.17.0
|
|
62
62
|
- Build package: org.openapitools.codegen.languages.PythonClientCodegen
|
|
63
63
|
|
{lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/RECORD
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
lunchmoney/__init__.py,sha256=
|
|
2
|
-
lunchmoney/api_client.py,sha256=
|
|
1
|
+
lunchmoney/__init__.py,sha256=ph9DotwKYRxIcqYRzStAY_Cj48_fTk09gLBU-xEhOUE,15137
|
|
2
|
+
lunchmoney/api_client.py,sha256=ayN8SwQViode6dySfjlMbdrtTze12Ot8_d6Q65pO2xU,30027
|
|
3
3
|
lunchmoney/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
|
|
4
|
-
lunchmoney/
|
|
4
|
+
lunchmoney/app.py,sha256=NSmXjsRvgoWE49OEKwbJ6AfP88v9VjF29JwXUkyLzII,22516
|
|
5
|
+
lunchmoney/configuration.py,sha256=PFHs2iGGHifUcTaBPIzWvTkeqove1-LXTJ0HLXhHwNg,21495
|
|
5
6
|
lunchmoney/exceptions.py,sha256=wwqToE7BTr_gLnBIhciaFC3gcLWTgecvw_n-LczGluc,8624
|
|
6
7
|
lunchmoney/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
8
|
lunchmoney/rest.py,sha256=srXSZvn9tu3_g7brC3IewCwaGDj1LOlz4nJ1-mDTtyA,8448
|
|
@@ -86,7 +87,7 @@ lunchmoney/models/update_transactions200_response.py,sha256=M2EGiFnVSgVTZxxwpfSE
|
|
|
86
87
|
lunchmoney/models/update_transactions_request.py,sha256=QFe3PXgGMyuD0fj7s5ZBKj_fwa9SmTig1EjC9bc-0mg,5570
|
|
87
88
|
lunchmoney/models/update_transactions_request_transactions_inner.py,sha256=l99tTbS64eraCuzpZWG0jRf069xIn08oha6PKezEysI,16674
|
|
88
89
|
lunchmoney/models/user_object.py,sha256=8Wz8vkuygfmkqJTu8hA-_l0usvkwvJMEyp7Wd7NYrzY,5899
|
|
89
|
-
lunchmoney_python_async-2.
|
|
90
|
-
lunchmoney_python_async-2.
|
|
91
|
-
lunchmoney_python_async-2.
|
|
92
|
-
lunchmoney_python_async-2.
|
|
90
|
+
lunchmoney_python_async-2.18.2.dist-info/METADATA,sha256=hpySPw51zFd0cDPVEABpDekkrs9DZZt-yNX89dZ7KWA,16481
|
|
91
|
+
lunchmoney_python_async-2.18.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
92
|
+
lunchmoney_python_async-2.18.2.dist-info/top_level.txt,sha256=zZag3NCnXp76Ay6xplmjroAfUH_hWWJzZNxTjxk3CdM,11
|
|
93
|
+
lunchmoney_python_async-2.18.2.dist-info/RECORD,,
|
{lunchmoney_python_async-2.10.0.dist-info → lunchmoney_python_async-2.18.2.dist-info}/top_level.txt
RENAMED
|
File without changes
|