Skip to content

Commit 74709ad

Browse files
committed
✨ Convert PrimeLunch to LunchableApp
1 parent 79b6f20 commit 74709ad

File tree

3 files changed

+43
-69
lines changed

3 files changed

+43
-69
lines changed

lunchable/plugins/base/base_app.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ class LunchableDataContainer(BaseModel):
5252
categories: Dict[int, CategoriesObject] = {}
5353
assets: Dict[int, AssetsObject] = {}
5454
tags: Dict[int, TagsObject] = {}
55-
user: Optional[UserObject] = None
55+
user: UserObject = UserObject(
56+
user_id=0, user_name="", user_email="", account_id=0, budget_name=""
57+
)
5658
crypto: Dict[int, UserObject] = {}
5759

5860
@property
@@ -121,7 +123,6 @@ def __init__(self, cache_time: int = 0, access_token: Optional[str] = None):
121123
"""
122124
self.lunch = LunchMoney(access_token=access_token)
123125
self.lunch_data = LunchableDataContainer()
124-
self.lunch_data.user = self.lunch.get_user()
125126
self.data_dir = FileConfig.DATA_DIR.joinpath(self.__class__.__name__).joinpath(
126127
sha256(self.lunch.access_token.encode("utf-8")).hexdigest()
127128
)
@@ -183,13 +184,18 @@ def _cache_single_object(
183184
return data_objects
184185

185186
def get_latest_cache(
186-
self, exclude: Optional[List[Type[LunchableModel]]] = None, force: bool = False
187+
self,
188+
include: Optional[List[Type[LunchableModel]]] = None,
189+
exclude: Optional[List[Type[LunchableModel]]] = None,
190+
force: bool = False,
187191
) -> None:
188192
"""
189193
Cache the Underlying Data Objects
190194
191195
Parameters
192196
----------
197+
include : Optional[List[Type[LunchableModel]]]
198+
Models to refresh cache for (instead of all of them)
193199
exclude : Optional[List[Type[LunchableModel]]]
194200
Models to skip cache refreshing
195201
force: bool
@@ -199,11 +205,15 @@ def get_latest_cache(
199205
-------
200206
None
201207
"""
202-
total_data_models: List[LunchableDataModel] = (
203-
self.lunchable_models + self.__builtin_data_models__
204-
)
208+
models_to_process = self.lunchable_models + self.__builtin_data_models__
209+
if include is not None:
210+
new_models_to_process: List[LunchableDataModel] = []
211+
data_model_mapping = {item.model: item for item in models_to_process}
212+
for model_class in include:
213+
new_models_to_process.append(data_model_mapping[model_class])
214+
models_to_process = new_models_to_process
205215
exclusions = exclude if exclude is not None else []
206-
for data_model in total_data_models:
216+
for data_model in models_to_process:
207217
if data_model.model in exclusions:
208218
continue
209219
cache = self._cache_single_object(

lunchable/plugins/base/pandas_app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def df_to_models(
5858
return [model_type(**item) for item in model_array]
5959

6060

61-
class PandasLunchableTransactionsApp(LunchableTransactionsApp, LunchablePandasApp):
61+
class LunchablePandasTransactionsApp(LunchableTransactionsApp, LunchablePandasApp):
6262
"""
6363
LunchableTransactionsApp with Pandas Super Powers
6464
"""

lunchable/plugins/primelunch/primelunch.py

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
from __future__ import annotations
66

77
import datetime
8+
import html
89
import logging
910
import os
1011
import pathlib
11-
from typing import Any, Dict, List, Optional
12+
from typing import Any, Dict, Optional
1213

1314
import click
1415
import numpy as np
@@ -17,14 +18,15 @@
1718
from rich import print, table
1819
from rich.prompt import Confirm
1920

20-
from lunchable import LunchMoney, TransactionUpdateObject
21+
from lunchable import TransactionUpdateObject
2122
from lunchable._config.logging_config import set_up_logging
22-
from lunchable.models import CategoriesObject, TransactionObject
23+
from lunchable.models import CategoriesObject, TransactionObject, UserObject
24+
from lunchable.plugins.base.pandas_app import LunchablePandasApp
2325

2426
logger = logging.getLogger(__name__)
2527

2628

27-
class PrimeLunch:
29+
class PrimeLunch(LunchablePandasApp):
2830
"""
2931
PrimeLunch: Amazon Notes Updater
3032
"""
@@ -38,59 +40,16 @@ def __init__(
3840
"""
3941
Initialize and set internal data
4042
"""
43+
super().__init__(cache_time=0, access_token=access_token)
4144
self.file_path = pathlib.Path(file_path)
4245
self.time_window = time_window
43-
self.transaction_map: Dict[int, TransactionObject] = {}
44-
self.cached = False
45-
self.lunch = LunchMoney(access_token=access_token)
46-
self.categories: dict[int, CategoriesObject] = {}
47-
48-
@staticmethod
49-
def transactions_to_df(transactions: List[TransactionObject]) -> pd.DataFrame:
50-
"""
51-
Convert Transactions Array to DataFrame
52-
53-
Parameters
54-
----------
55-
transactions: List[TransactionObject]
56-
57-
Returns
58-
-------
59-
pd.DataFrame
60-
"""
61-
return pd.DataFrame(
62-
[item.dict() for item in transactions],
63-
columns=TransactionObject.__fields__.keys(),
64-
)
65-
66-
@staticmethod
67-
def df_to_transactions(df: pd.DataFrame) -> List[TransactionObject]:
68-
"""
69-
Convert DataFrame to Transaction Array
70-
71-
Parameters
72-
----------
73-
df: pd.DataFrame
74-
75-
Returns
76-
-------
77-
List[TransactionObject]
78-
"""
79-
array_df = df.copy()
80-
array_df = array_df.fillna(np.nan).replace([np.nan], [None])
81-
transaction_array = array_df.to_dict(orient="records")
82-
return [TransactionObject(**item) for item in transaction_array]
8346

8447
def amazon_to_df(self) -> pd.DataFrame:
8548
"""
8649
Read an Amazon Data File to a DataFrame
8750
8851
This is pretty simple, except duplicate header rows need to be cleaned
8952
90-
Parameters
91-
----------
92-
file_path: os.PathLike
93-
9453
Returns
9554
-------
9655
pd.DataFrame
@@ -286,15 +245,17 @@ def cache_transactions(
286245
start_date,
287246
end_cache_date,
288247
)
289-
transactions = self.lunch.get_transactions(
290-
start_date=start_date, end_date=end_cache_date
248+
self.get_latest_cache(include=[CategoriesObject, UserObject])
249+
self.refresh_transactions(start_date=start_date, end_date=end_cache_date)
250+
logger.info(
251+
'Scanning LunchMoney Budget: "%s"',
252+
html.unescape(self.lunch_data.user.budget_name), # type: ignore[union-attr]
291253
)
292-
transaction_map = {item.id: item for item in transactions}
293-
self.categories = {item.id: item for item in self.lunch.get_categories()}
294-
self.transaction_map = transaction_map
295-
self.cached = True
296-
logger.info("%s transactions returned from LunchMoney", len(transactions))
297-
return transaction_map
254+
logger.info(
255+
"%s transactions returned from LunchMoney",
256+
len(self.lunch_data.transactions),
257+
)
258+
return self.lunch_data.transactions
298259

299260
def print_transaction(
300261
self, transaction: TransactionObject, former_transaction: TransactionObject
@@ -312,7 +273,8 @@ def print_transaction(
312273
)
313274
if former_transaction.category_id is not None:
314275
transaction_table.add_row(
315-
"📊 Category", self.categories[former_transaction.category_id].name
276+
"📊 Category",
277+
self.lunch_data.categories[former_transaction.category_id].name,
316278
)
317279
if (
318280
former_transaction.original_name is not None
@@ -345,7 +307,7 @@ def update_transaction(
345307
-------
346308
Optional[Dict[str, Any]]
347309
"""
348-
former_transaction = self.transaction_map[transaction.id]
310+
former_transaction = self.lunch_data.transactions[transaction.id]
349311
response = None
350312
stripped_notes = transaction.notes.strip() # type: ignore[union-attr]
351313
acceptable_length = min(349, len(stripped_notes))
@@ -386,16 +348,18 @@ def process_transactions(self, confirm: bool = True):
386348
max_date,
387349
)
388350
self.cache_transactions(start_date=min_date, end_date=max_date)
389-
transaction_df = self.transactions_to_df(
390-
transactions=list(self.transaction_map.values())
351+
transaction_df = self.models_to_df(
352+
models=self.lunch_data.transactions.values(),
391353
)
392354
amazon_transaction_df = self.filter_amazon_transactions(df=transaction_df)
393355
merged_data = self.merge_transactions(
394356
transactions=amazon_transaction_df,
395357
amazon=amazon_df,
396358
time_range=self.time_window,
397359
)
398-
updated_transactions = self.df_to_transactions(df=merged_data)
360+
updated_transactions = self.df_to_models(
361+
df=merged_data, model_type=TransactionObject
362+
)
399363
responses = []
400364
for item in updated_transactions:
401365
resp = self.update_transaction(transaction=item, confirm=confirm)

0 commit comments

Comments
 (0)