tbr-deal-finder 0.3.1__py3-none-any.whl → 0.3.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.
- tbr_deal_finder/__init__.py +1 -1
- tbr_deal_finder/gui/main.py +29 -0
- tbr_deal_finder/gui/pages/all_books.py +7 -0
- tbr_deal_finder/gui/pages/base_book_page.py +1 -2
- tbr_deal_finder/gui/pages/latest_deals.py +6 -0
- tbr_deal_finder/gui/pages/settings.py +1 -0
- tbr_deal_finder/retailer_deal.py +20 -29
- tbr_deal_finder/utils.py +28 -19
- {tbr_deal_finder-0.3.1.dist-info → tbr_deal_finder-0.3.2.dist-info}/METADATA +1 -1
- {tbr_deal_finder-0.3.1.dist-info → tbr_deal_finder-0.3.2.dist-info}/RECORD +13 -13
- {tbr_deal_finder-0.3.1.dist-info → tbr_deal_finder-0.3.2.dist-info}/WHEEL +0 -0
- {tbr_deal_finder-0.3.1.dist-info → tbr_deal_finder-0.3.2.dist-info}/entry_points.txt +0 -0
- {tbr_deal_finder-0.3.1.dist-info → tbr_deal_finder-0.3.2.dist-info}/licenses/LICENSE +0 -0
tbr_deal_finder/__init__.py
CHANGED
tbr_deal_finder/gui/main.py
CHANGED
@@ -26,6 +26,7 @@ class TBRDealFinderApp:
|
|
26
26
|
self.current_page = "all_deals"
|
27
27
|
self.selected_book = None
|
28
28
|
self.update_info = None # Store update information
|
29
|
+
self.nav_disabled = False # Track navigation disabled state
|
29
30
|
|
30
31
|
# Initialize pages
|
31
32
|
self.settings_page = SettingsPage(self)
|
@@ -258,6 +259,16 @@ class TBRDealFinderApp:
|
|
258
259
|
|
259
260
|
def nav_changed(self, e):
|
260
261
|
"""Handle navigation rail selection changes"""
|
262
|
+
# Prevent navigation if disabled
|
263
|
+
if self.nav_disabled:
|
264
|
+
# Reset to current page selection to prevent visual change
|
265
|
+
current_indices = {"all_deals": 0, "latest_deals": 1, "all_books": 2}
|
266
|
+
self.nav_rail.selected_index = current_indices.get(self.current_page, 0)
|
267
|
+
# Reapply disabled state after page update
|
268
|
+
self.nav_rail.disabled = True
|
269
|
+
self.page.update()
|
270
|
+
return
|
271
|
+
|
261
272
|
destinations = ["all_deals", "latest_deals", "all_books"]
|
262
273
|
if e.control.selected_index < len(destinations):
|
263
274
|
self.current_page = destinations[e.control.selected_index]
|
@@ -289,6 +300,23 @@ class TBRDealFinderApp:
|
|
289
300
|
self.latest_deals_page.refresh_page_state()
|
290
301
|
self.all_books_page.refresh_page_state()
|
291
302
|
|
303
|
+
def disable_navigation(self):
|
304
|
+
"""Disable navigation rail during background operations"""
|
305
|
+
self.nav_disabled = True
|
306
|
+
if hasattr(self, 'nav_rail'):
|
307
|
+
self.nav_rail.disabled = True
|
308
|
+
self.page.update()
|
309
|
+
|
310
|
+
def enable_navigation(self):
|
311
|
+
"""Enable navigation rail after background operations complete"""
|
312
|
+
if not self.nav_disabled:
|
313
|
+
return
|
314
|
+
|
315
|
+
self.nav_disabled = False
|
316
|
+
if hasattr(self, 'nav_rail'):
|
317
|
+
self.nav_rail.disabled = False
|
318
|
+
self.page.update()
|
319
|
+
|
292
320
|
def get_current_page_content(self):
|
293
321
|
"""Get content for the current page"""
|
294
322
|
if self.config is None and self.current_page != "settings":
|
@@ -309,6 +337,7 @@ class TBRDealFinderApp:
|
|
309
337
|
|
310
338
|
def get_config_prompt(self):
|
311
339
|
"""Show config setup prompt when no config exists"""
|
340
|
+
self.disable_navigation()
|
312
341
|
return ft.Container(
|
313
342
|
content=ft.Column([
|
314
343
|
ft.Icon(ft.Icons.SETTINGS, size=64, color=ft.Colors.GREY_400),
|
@@ -39,6 +39,9 @@ class AllBooksPage(BaseBookPage):
|
|
39
39
|
|
40
40
|
async def _async_load_items(self):
|
41
41
|
"""Load TBR books asynchronously using Flet's async support"""
|
42
|
+
# Disable navigation during the loading operation
|
43
|
+
self.app.disable_navigation()
|
44
|
+
|
42
45
|
try:
|
43
46
|
# Run the async operation directly
|
44
47
|
await self.app.auth_all_configured_retailers()
|
@@ -50,6 +53,10 @@ class AllBooksPage(BaseBookPage):
|
|
50
53
|
self.filtered_items = []
|
51
54
|
finally:
|
52
55
|
self.set_loading(False)
|
56
|
+
|
57
|
+
# Re-enable navigation after the operation completes
|
58
|
+
self.app.enable_navigation()
|
59
|
+
|
53
60
|
# Update the page to reflect the loaded data
|
54
61
|
self.app.page.update()
|
55
62
|
|
@@ -142,8 +142,7 @@ class BaseBookPage(ABC):
|
|
142
142
|
item_tiles.append(tile)
|
143
143
|
|
144
144
|
return ft.Container(
|
145
|
-
content=ft.
|
146
|
-
height=700,
|
145
|
+
content=ft.Column(item_tiles, spacing=5),
|
147
146
|
border=ft.border.all(1, ft.Colors.OUTLINE),
|
148
147
|
border_radius=8,
|
149
148
|
padding=10
|
@@ -274,6 +274,9 @@ class LatestDealsPage(BaseBookPage):
|
|
274
274
|
self.progress_container.visible = True
|
275
275
|
self.run_button.disabled = True
|
276
276
|
|
277
|
+
# Disable navigation during the operation
|
278
|
+
self.app.disable_navigation()
|
279
|
+
|
277
280
|
# Update the page to show loading state
|
278
281
|
self.app.page.update()
|
279
282
|
|
@@ -299,6 +302,9 @@ class LatestDealsPage(BaseBookPage):
|
|
299
302
|
self.run_button.disabled = False
|
300
303
|
self.check_last_run() # Refresh the status
|
301
304
|
|
305
|
+
# Re-enable navigation after the operation
|
306
|
+
self.app.enable_navigation()
|
307
|
+
|
302
308
|
# Update the page to reset loading state
|
303
309
|
self.app.page.update()
|
304
310
|
|
tbr_deal_finder/retailer_deal.py
CHANGED
@@ -2,16 +2,14 @@ import asyncio
|
|
2
2
|
import copy
|
3
3
|
from collections import defaultdict
|
4
4
|
|
5
|
-
import click
|
6
5
|
import pandas as pd
|
7
|
-
from tqdm.asyncio import tqdm_asyncio
|
8
6
|
|
9
7
|
from tbr_deal_finder.book import Book, get_active_deals, BookFormat
|
10
8
|
from tbr_deal_finder.config import Config
|
11
9
|
from tbr_deal_finder.tracked_books import get_tbr_books, get_unknown_books, set_unknown_books
|
12
10
|
from tbr_deal_finder.retailer import RETAILER_MAP
|
13
11
|
from tbr_deal_finder.retailer.models import Retailer
|
14
|
-
from tbr_deal_finder.utils import get_duckdb_conn, echo_info, echo_err
|
12
|
+
from tbr_deal_finder.utils import get_duckdb_conn, echo_info, echo_err
|
15
13
|
|
16
14
|
|
17
15
|
def update_retailer_deal_table(config: Config, new_deals: list[Book]):
|
@@ -67,6 +65,13 @@ async def _get_books(
|
|
67
65
|
Returns:
|
68
66
|
List of Book objects with updated pricing and availability
|
69
67
|
"""
|
68
|
+
|
69
|
+
echo_info(f"Getting deals from {retailer.name}")
|
70
|
+
books = _get_retailer_relevant_tbr_books(
|
71
|
+
retailer,
|
72
|
+
books,
|
73
|
+
)
|
74
|
+
|
70
75
|
semaphore = asyncio.Semaphore(retailer.max_concurrency)
|
71
76
|
response = []
|
72
77
|
unknown_books = []
|
@@ -81,11 +86,7 @@ async def _get_books(
|
|
81
86
|
if book.deal_id not in ignored_deal_ids
|
82
87
|
]
|
83
88
|
|
84
|
-
|
85
|
-
results = await asyncio.gather(*tasks)
|
86
|
-
else:
|
87
|
-
results = await tqdm_asyncio.gather(*tasks, desc=f"Getting latest prices from {retailer.name}")
|
88
|
-
|
89
|
+
results = await asyncio.gather(*tasks)
|
89
90
|
for book in results:
|
90
91
|
if not book:
|
91
92
|
"""Cases where we know the retailer has the book but it's not coming back.
|
@@ -99,9 +100,7 @@ async def _get_books(
|
|
99
100
|
elif not book.exists:
|
100
101
|
unknown_books.append(book)
|
101
102
|
|
102
|
-
|
103
|
-
for book in unknown_books:
|
104
|
-
echo_info(f"{book.title} by {book.authors} not found")
|
103
|
+
echo_info(f"Finished getting deals from {retailer.name}")
|
105
104
|
|
106
105
|
return response, unknown_books
|
107
106
|
|
@@ -184,34 +183,26 @@ async def _get_latest_deals(config: Config):
|
|
184
183
|
ignore_books: list[Book] = get_unknown_books(config)
|
185
184
|
ignored_deal_ids: set[str] = {book.deal_id for book in ignore_books}
|
186
185
|
|
186
|
+
tasks = []
|
187
187
|
for retailer_str in config.tracked_retailers:
|
188
188
|
retailer = RETAILER_MAP[retailer_str]()
|
189
189
|
await retailer.set_auth()
|
190
190
|
|
191
|
-
|
192
|
-
|
193
|
-
|
191
|
+
tasks.append(
|
192
|
+
_get_books(
|
193
|
+
config,
|
194
|
+
retailer,
|
195
|
+
tbr_books,
|
196
|
+
ignored_deal_ids
|
197
|
+
)
|
194
198
|
)
|
195
199
|
|
196
|
-
|
197
|
-
|
198
|
-
retailer_books, u_books = await _get_books(
|
199
|
-
config,
|
200
|
-
retailer,
|
201
|
-
relevant_tbr_books,
|
202
|
-
ignored_deal_ids
|
203
|
-
)
|
200
|
+
results = await asyncio.gather(*tasks)
|
201
|
+
for retailer_books, u_books in results:
|
204
202
|
books.extend(retailer_books)
|
205
203
|
unknown_books.extend(u_books)
|
206
|
-
click.echo("---------------\n")
|
207
204
|
|
208
205
|
_apply_proper_list_prices(books)
|
209
|
-
|
210
|
-
books = [
|
211
|
-
book
|
212
|
-
for book in books
|
213
|
-
]
|
214
|
-
|
215
206
|
update_retailer_deal_table(config, books)
|
216
207
|
set_unknown_books(config, unknown_books)
|
217
208
|
|
tbr_deal_finder/utils.py
CHANGED
@@ -27,25 +27,34 @@ def get_data_dir() -> Path:
|
|
27
27
|
app_name = "TBR Deal Finder"
|
28
28
|
|
29
29
|
if custom_path := os.getenv("TBR_DEAL_FINDER_CUSTOM_PATH"):
|
30
|
-
path = Path(custom_path)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
30
|
+
path = Path(custom_path).expanduser()
|
31
|
+
else:
|
32
|
+
cli_path = Path.home() / ".tbr_deal_finder"
|
33
|
+
if sys.platform == "win32":
|
34
|
+
# Windows: C:\Users\Username\AppData\Local\AppAuthor\AppName
|
35
|
+
base = os.environ.get("LOCALAPPDATA", os.path.expanduser("~\\AppData\\Local"))
|
36
|
+
gui_path = Path(base) / app_author / app_name
|
37
|
+
|
38
|
+
elif sys.platform == "darwin":
|
39
|
+
# macOS: ~/Library/Application Support/AppName
|
40
|
+
gui_path = Path.home() / "Library" / "Application Support" / app_name
|
41
|
+
|
42
|
+
else: # Linux and others
|
43
|
+
# Linux: ~/.local/share/appname (following XDG spec)
|
44
|
+
xdg_data_home = os.environ.get("XDG_DATA_HOME",
|
45
|
+
os.path.expanduser("~/.local/share"))
|
46
|
+
gui_path = Path(xdg_data_home) / app_name.lower()
|
47
|
+
|
48
|
+
if is_gui_env():
|
49
|
+
path = gui_path
|
50
|
+
if cli_path.exists() and not path.exists():
|
51
|
+
# Use the cli path if it exists and the gui path does not
|
52
|
+
path = cli_path
|
53
|
+
else:
|
54
|
+
path = cli_path
|
55
|
+
if gui_path.exists() and not path.exists():
|
56
|
+
# Use the gui path if it exists and the cli path does not
|
57
|
+
path = gui_path
|
49
58
|
|
50
59
|
# Create directory if it doesn't exist
|
51
60
|
path.mkdir(parents=True, exist_ok=True)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
tbr_deal_finder/__init__.py,sha256=
|
1
|
+
tbr_deal_finder/__init__.py,sha256=3Zo2T2Th8AT8qexLKWZhNJIeyXPVf2N76kkDGJw5Wc0,104
|
2
2
|
tbr_deal_finder/__main__.py,sha256=b2-3WiGIno_XVusUDijQXa2MthKahFz6CVH-hIBe7es,165
|
3
3
|
tbr_deal_finder/book.py,sha256=WbvDEeI923iQX_eIsC9H7y-qKVPkfCqwjBCqChssFCM,6740
|
4
4
|
tbr_deal_finder/cli.py,sha256=16vZnWS9TTWPhIjZsjsf7OY7Btb0ULOfws5EkUefbRY,7089
|
@@ -6,19 +6,19 @@ tbr_deal_finder/config.py,sha256=pocHMEoJVIoiuRzEtKDAmsGmiiLgD-lN6n6TU9gI8Lc,398
|
|
6
6
|
tbr_deal_finder/desktop_updater.py,sha256=VCD4MW1ckx6yyq0N_CJrOth8wePqPhTjhvdJTKsUVkw,5123
|
7
7
|
tbr_deal_finder/migrations.py,sha256=fO7r2JbWb6YG0CsPqauakwvbKaEFPxqX1PP8c8N03Wc,4951
|
8
8
|
tbr_deal_finder/owned_books.py,sha256=Cf1VeiSg7XBi_TXptJfy5sO1mEgMMQWbJ_P6SzAx0nQ,516
|
9
|
-
tbr_deal_finder/retailer_deal.py,sha256=
|
9
|
+
tbr_deal_finder/retailer_deal.py,sha256=l2n-79kSNZfPh73PZBbSE6tkNYYCaHPEehIPcelDPeY,7214
|
10
10
|
tbr_deal_finder/tracked_books.py,sha256=u3KfBNlwvsEwTfM5TAJVLbiTmm1lTe2k70JJOszQz1k,12714
|
11
|
-
tbr_deal_finder/utils.py,sha256=
|
11
|
+
tbr_deal_finder/utils.py,sha256=LJYRNPRO3XBFIlodGzxCxlxiQ9viNYGU5QjNnjz4qMA,3635
|
12
12
|
tbr_deal_finder/version_check.py,sha256=tzuKWngnjSdjPAOdyuPk2ym6Lv3LyeObJAfgsBIH9YQ,1217
|
13
13
|
tbr_deal_finder/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
tbr_deal_finder/gui/main.py,sha256=
|
14
|
+
tbr_deal_finder/gui/main.py,sha256=VvdCdSH3mbVhw_3PaDD78U8RQV-drTyXyCNSWFwmoe8,27539
|
15
15
|
tbr_deal_finder/gui/pages/__init__.py,sha256=1xc9Ib3oX_hqxLdhZvbCv3AhwOmVBSklZZC6CAcvoMU,20
|
16
|
-
tbr_deal_finder/gui/pages/all_books.py,sha256=
|
16
|
+
tbr_deal_finder/gui/pages/all_books.py,sha256=uQguQ9_NjM5kq_D5DbOWFB9om1lOdPk1iTcjH0yl9bg,3655
|
17
17
|
tbr_deal_finder/gui/pages/all_deals.py,sha256=rY08w4XXFc_Jbd39BPJ6fO00vstuuu2YHvZterHx5ZI,2389
|
18
|
-
tbr_deal_finder/gui/pages/base_book_page.py,sha256=
|
18
|
+
tbr_deal_finder/gui/pages/base_book_page.py,sha256=kmHjJE4eIGrMhXtBWePn_eXfO6qJ36kIoQ7hE1xB3ic,10465
|
19
19
|
tbr_deal_finder/gui/pages/book_details.py,sha256=POknUa9yNjfqhC6eXuw7RtaRcFtQj_CdvJ8mK2r6DDo,22047
|
20
|
-
tbr_deal_finder/gui/pages/latest_deals.py,sha256=
|
21
|
-
tbr_deal_finder/gui/pages/settings.py,sha256=
|
20
|
+
tbr_deal_finder/gui/pages/latest_deals.py,sha256=E-DoINTb7hAdiope7y9kqCdWuViBq3zDmMmFEL-vzso,14019
|
21
|
+
tbr_deal_finder/gui/pages/settings.py,sha256=gdQXi508yd_oZ5lj0mxuDL5qSxmH256LwPi5gTo2Cqg,14357
|
22
22
|
tbr_deal_finder/queries/get_active_deals.sql,sha256=nh0F1lRV6YVrUV7gsQpjsgfXmN9R0peBeMHRifjgpUM,212
|
23
23
|
tbr_deal_finder/queries/get_deals_found_at.sql,sha256=KqrtQk7FS4Hf74RyL1r-oD2D-RJz1urrxKxkwlvjAro,139
|
24
24
|
tbr_deal_finder/queries/latest_deal_last_ran_most_recent_success.sql,sha256=W4cNMAHtcW2DzQyPL8SHHFcbVZQKVK2VfTzazxC3LJU,107
|
@@ -31,8 +31,8 @@ tbr_deal_finder/retailer/chirp.py,sha256=GR8yeXp-1DHMktepy5TecHslrUibpLM7LfueIuT
|
|
31
31
|
tbr_deal_finder/retailer/kindle.py,sha256=kA4SO2kl2SvJHADkBYyYMgq_bditStbiTiW7piQPAFI,5282
|
32
32
|
tbr_deal_finder/retailer/librofm.py,sha256=dn1kaJEQ9g_AOAFNKhUtFLxYxQZa6RAHgXhT_dx-O3k,7514
|
33
33
|
tbr_deal_finder/retailer/models.py,sha256=56xTwcLcw3bFcvTDOb85TktqtksvvyY95hZBbp9-5mY,3340
|
34
|
-
tbr_deal_finder-0.3.
|
35
|
-
tbr_deal_finder-0.3.
|
36
|
-
tbr_deal_finder-0.3.
|
37
|
-
tbr_deal_finder-0.3.
|
38
|
-
tbr_deal_finder-0.3.
|
34
|
+
tbr_deal_finder-0.3.2.dist-info/METADATA,sha256=W3GS1LYeE-KXn0aE9S8d4v7GtRGTAobTw4ZeUmXYtqw,3448
|
35
|
+
tbr_deal_finder-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
36
|
+
tbr_deal_finder-0.3.2.dist-info/entry_points.txt,sha256=xjeRw7aX_jbX1ERC--IgYIa2oLNgeRefsMbKeTAVb70,112
|
37
|
+
tbr_deal_finder-0.3.2.dist-info/licenses/LICENSE,sha256=rNc0wNPn4d4HHu6ZheJzeUaz_FbJ4rj2Dr2FjAivkNg,1064
|
38
|
+
tbr_deal_finder-0.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|