tbr-deal-finder 0.2.0__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.
Files changed (34) hide show
  1. tbr_deal_finder/__init__.py +1 -5
  2. tbr_deal_finder/__main__.py +7 -0
  3. tbr_deal_finder/book.py +28 -8
  4. tbr_deal_finder/cli.py +15 -28
  5. tbr_deal_finder/config.py +3 -3
  6. tbr_deal_finder/desktop_updater.py +147 -0
  7. tbr_deal_finder/gui/__init__.py +0 -0
  8. tbr_deal_finder/gui/main.py +754 -0
  9. tbr_deal_finder/gui/pages/__init__.py +1 -0
  10. tbr_deal_finder/gui/pages/all_books.py +100 -0
  11. tbr_deal_finder/gui/pages/all_deals.py +63 -0
  12. tbr_deal_finder/gui/pages/base_book_page.py +290 -0
  13. tbr_deal_finder/gui/pages/book_details.py +604 -0
  14. tbr_deal_finder/gui/pages/latest_deals.py +376 -0
  15. tbr_deal_finder/gui/pages/settings.py +390 -0
  16. tbr_deal_finder/migrations.py +26 -0
  17. tbr_deal_finder/queries/latest_unknown_book_sync.sql +5 -0
  18. tbr_deal_finder/retailer/amazon.py +60 -9
  19. tbr_deal_finder/retailer/amazon_custom_auth.py +79 -0
  20. tbr_deal_finder/retailer/audible.py +2 -1
  21. tbr_deal_finder/retailer/chirp.py +55 -11
  22. tbr_deal_finder/retailer/kindle.py +68 -44
  23. tbr_deal_finder/retailer/librofm.py +58 -21
  24. tbr_deal_finder/retailer/models.py +31 -1
  25. tbr_deal_finder/retailer_deal.py +69 -37
  26. tbr_deal_finder/tracked_books.py +76 -8
  27. tbr_deal_finder/utils.py +74 -3
  28. tbr_deal_finder/version_check.py +40 -0
  29. {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/METADATA +19 -88
  30. tbr_deal_finder-0.3.2.dist-info/RECORD +38 -0
  31. {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/entry_points.txt +1 -0
  32. tbr_deal_finder-0.2.0.dist-info/RECORD +0 -24
  33. {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/WHEEL +0 -0
  34. {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,9 @@
1
1
  import asyncio
2
2
  import copy
3
3
  import csv
4
+ import functools
4
5
  from collections import defaultdict
6
+ from datetime import datetime, timedelta
5
7
  from typing import Callable, Awaitable, Optional
6
8
 
7
9
  import pandas as pd
@@ -12,7 +14,7 @@ from tbr_deal_finder.owned_books import get_owned_books
12
14
  from tbr_deal_finder.retailer import Chirp, RETAILER_MAP, LibroFM, Kindle
13
15
  from tbr_deal_finder.config import Config
14
16
  from tbr_deal_finder.retailer.models import Retailer
15
- from tbr_deal_finder.utils import execute_query, get_duckdb_conn
17
+ from tbr_deal_finder.utils import execute_query, get_duckdb_conn, get_query_by_name, is_gui_env
16
18
 
17
19
 
18
20
  def _library_export_tbr_books(config: Config, tbr_book_map: dict[str: Book]):
@@ -137,16 +139,20 @@ async def _set_tbr_book_attr(
137
139
  tbr_books_map = {b.full_title_str: b for b in tbr_books}
138
140
  tbr_books_copy = copy.deepcopy(tbr_books)
139
141
  semaphore = asyncio.Semaphore(5)
140
- human_readable_name = target_attr.replace("_", " ").title()
141
142
 
142
143
  # Get books with the appropriate transform applied
143
144
  # Responsibility is on the callable here
144
- enriched_books = await tqdm_asyncio.gather(
145
- *[
146
- get_book_callable(book, semaphore) for book in tbr_books_copy
147
- ],
148
- desc=f"Getting required {human_readable_name} info"
149
- )
145
+ tasks = [
146
+ get_book_callable(book, semaphore) for book in tbr_books_copy
147
+ ]
148
+ if is_gui_env():
149
+ enriched_books = await asyncio.gather(*tasks)
150
+ else:
151
+ human_readable_name = target_attr.replace("_", " ").title()
152
+ enriched_books = await tqdm_asyncio.gather(
153
+ *tasks,
154
+ desc=f"Getting required {human_readable_name} info"
155
+ )
150
156
  for enriched_book in enriched_books:
151
157
  book = tbr_books_map[enriched_book.full_title_str]
152
158
  setattr(
@@ -214,6 +220,68 @@ async def _maybe_set_audiobook_isbn(config: Config, new_tbr_books: list[Book]):
214
220
  )
215
221
 
216
222
 
223
+ @functools.cache
224
+ def unknown_books_requires_sync() -> bool:
225
+ db_conn = get_duckdb_conn()
226
+ results = execute_query(
227
+ db_conn,
228
+ get_query_by_name("latest_unknown_book_sync.sql")
229
+ )
230
+ if not results:
231
+ return True
232
+
233
+ sync_last_ran = results[0]["timepoint"]
234
+ return datetime.now() - timedelta(days=7) > sync_last_ran
235
+
236
+
237
+ def clear_unknown_books():
238
+ db_conn = get_duckdb_conn()
239
+ db_conn.execute(
240
+ "DELETE FROM unknown_book"
241
+ )
242
+ db_conn.execute(
243
+ "DELETE FROM unknown_book_run_history"
244
+ )
245
+
246
+
247
+ def set_unknown_books(config: Config, unknown_books: list[Book]):
248
+ if (not unknown_books_requires_sync()) and (not unknown_books):
249
+ return
250
+
251
+ db_conn = get_duckdb_conn()
252
+
253
+ if unknown_books_requires_sync():
254
+ db_conn.execute(
255
+ "INSERT INTO unknown_book_run_history (timepoint, ran_successfully, details) VALUES (?, ?, ?)",
256
+ [config.run_time, True, ""]
257
+ )
258
+
259
+ db_conn.execute(
260
+ "DELETE FROM unknown_book"
261
+ )
262
+ if not unknown_books:
263
+ return
264
+
265
+ df = pd.DataFrame([book.unknown_book_dict() for book in unknown_books])
266
+ db_conn = get_duckdb_conn()
267
+ db_conn.register("_df", df)
268
+ db_conn.execute("INSERT INTO unknown_book SELECT * FROM _df;")
269
+ db_conn.unregister("_df")
270
+
271
+
272
+ def get_unknown_books(config: Config) -> list[Book]:
273
+ if unknown_books_requires_sync():
274
+ return []
275
+
276
+ db_conn = get_duckdb_conn()
277
+ unknown_book_data = execute_query(
278
+ db_conn,
279
+ "SELECT * EXCLUDE(book_id) FROM unknown_book"
280
+ )
281
+
282
+ return [Book(timepoint=config.run_time, **b) for b in unknown_book_data]
283
+
284
+
217
285
  async def _maybe_set_ebook_asin(config: Config, new_tbr_books: list[Book]):
218
286
  """To get the price from kindle for a book, you need its asin
219
287
  """
tbr_deal_finder/utils.py CHANGED
@@ -1,11 +1,64 @@
1
+ import datetime
2
+ import functools
3
+ import os
1
4
  import re
5
+ import sys
6
+ from pathlib import Path
2
7
  from typing import Optional
3
8
 
4
9
  import click
5
10
  import duckdb
6
11
 
7
- from tbr_deal_finder import TBR_DEALS_PATH, QUERY_PATH
8
-
12
+ from tbr_deal_finder import QUERY_PATH
13
+
14
+
15
+ @functools.cache
16
+ def is_gui_env() -> bool:
17
+ return os.environ.get("ENTRYPOINT", "GUI") == "GUI"
18
+
19
+
20
+ @functools.cache
21
+ def get_data_dir() -> Path:
22
+ """
23
+ Get the appropriate user data directory for each platform
24
+ following OS conventions
25
+ """
26
+ app_author = "WillNye"
27
+ app_name = "TBR Deal Finder"
28
+
29
+ if custom_path := os.getenv("TBR_DEAL_FINDER_CUSTOM_PATH"):
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
58
+
59
+ # Create directory if it doesn't exist
60
+ path.mkdir(parents=True, exist_ok=True)
61
+ return path
9
62
 
10
63
  def currency_to_float(price_str):
11
64
  """Parse various price formats to float."""
@@ -21,8 +74,13 @@ def currency_to_float(price_str):
21
74
  return 0.0
22
75
 
23
76
 
77
+ def float_to_currency(val: float) -> str:
78
+ from tbr_deal_finder.config import Config
79
+ return f"{Config.currency_symbol()}{val:.2f}"
80
+
81
+
24
82
  def get_duckdb_conn():
25
- return duckdb.connect(TBR_DEALS_PATH.joinpath("tbr_deal_finder.db"))
83
+ return duckdb.connect(get_data_dir().joinpath("tbr_deal_finder.db"))
26
84
 
27
85
 
28
86
  def execute_query(
@@ -37,6 +95,19 @@ def execute_query(
37
95
  return [dict(zip(column_names, row)) for row in rows]
38
96
 
39
97
 
98
+ def get_latest_deal_last_ran(
99
+ db_conn: duckdb.DuckDBPyConnection
100
+ ) -> Optional[datetime.datetime]:
101
+
102
+ results = execute_query(
103
+ db_conn,
104
+ QUERY_PATH.joinpath("latest_deal_last_ran_most_recent_success.sql").read_text(),
105
+ )
106
+ if not results:
107
+ return None
108
+ return results[0]["timepoint"]
109
+
110
+
40
111
  def get_query_by_name(file_name: str) -> str:
41
112
  return QUERY_PATH.joinpath(file_name).read_text()
42
113
 
@@ -0,0 +1,40 @@
1
+ import requests
2
+ from packaging import version
3
+ import warnings
4
+ from tbr_deal_finder import __VERSION__
5
+
6
+ _PACKAGE_NAME = "tbr-deal-finder"
7
+
8
+ def check_for_updates():
9
+ """Check if a newer version is available on PyPI."""
10
+ current_version = __VERSION__
11
+
12
+ try:
13
+ response = requests.get(
14
+ f"https://pypi.org/pypi/{_PACKAGE_NAME}/json",
15
+ timeout=2 # Don't hang if PyPI is slow
16
+ )
17
+ response.raise_for_status()
18
+
19
+ latest_version = response.json()["info"]["version"]
20
+
21
+ if version.parse(latest_version) > version.parse(current_version):
22
+ return latest_version
23
+ return None
24
+
25
+ except Exception:
26
+ # Silently fail - don't break user's code over version check
27
+ return None
28
+
29
+
30
+ def notify_if_outdated():
31
+ """Show a warning if package is outdated."""
32
+ latest = check_for_updates()
33
+ if latest:
34
+ warnings.warn(
35
+ f"A new version of {_PACKAGE_NAME} is available ({latest}). "
36
+ f"You have {__VERSION__}. Consider upgrading:\n"
37
+ f"pip install --upgrade {_PACKAGE_NAME}\nOr if you're running using uv:\ngit checkout main && git pull",
38
+ UserWarning,
39
+ stacklevel=2
40
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tbr-deal-finder
3
- Version: 0.2.0
3
+ Version: 0.3.2
4
4
  Summary: Track price drops and find deals on books in your TBR list across audiobook and ebook formats.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -9,26 +9,32 @@ Requires-Dist: aiohttp>=3.12.14
9
9
  Requires-Dist: audible==0.8.2
10
10
  Requires-Dist: click>=8.2.1
11
11
  Requires-Dist: duckdb>=1.3.2
12
+ Requires-Dist: flet[all]==0.28.2
12
13
  Requires-Dist: levenshtein>=0.27.1
14
+ Requires-Dist: pandas-stubs==2.3.0.250703
13
15
  Requires-Dist: pandas>=2.3.1
16
+ Requires-Dist: plotly>=5.17.0
17
+ Requires-Dist: pyinstaller>=6.15.0
14
18
  Requires-Dist: questionary>=2.1.0
15
19
  Requires-Dist: tqdm>=4.67.1
16
20
  Requires-Dist: unidecode>=1.4.0
17
21
  Description-Content-Type: text/markdown
18
22
 
19
- # tbr-deal-finder
20
-
23
+ <div>
24
+ <h1>TBR Deal Finder <img src="assets/icon.png" alt="TBR Deal Finder" width="128" height="128" style="vertical-align: middle; margin-left: 10px;"></h1>
25
+ </div>
21
26
  Track price drops and find deals on books in your TBR (To Be Read) and wishlist across digital book retailers.
22
27
 
23
28
  ## Features
24
29
  - Use your StoryGraph exports, Goodreads exports, and custom csvs (spreadsheet) to track book deals
25
- - Supports multiple of the library exports above
26
- - Tracks deals on the wishlist of all your configured retailers like audible
27
- - Supports multiple locales and currencies
30
+ - Supports multiple library exports
31
+ - Tracks deals on the wishlist of all your configured retailers like audible (excluding kindle)
32
+ - Supports multiple locales
28
33
  - Find the latest and active deals from supported sellers
29
- - Simple CLI interface for setup and usage
30
34
  - Only get notified for new deals or view all active deals
31
35
  - Filters out books you already own to prevent purchasing the same book on multiple retailers
36
+ - Track historical pricing* (History limited to your runs making it more accurate over time)
37
+ - Compare pricing across digital retailers
32
38
 
33
39
  ## Support
34
40
 
@@ -53,24 +59,6 @@ Track price drops and find deals on books in your TBR (To Be Read) and wishlist
53
59
  * ES
54
60
  * BR
55
61
 
56
- ## Installation Guide
57
-
58
- ### Python (Recommended)
59
- 1. If it's not already on your computer, download Python https://www.python.org/downloads/
60
- 1. tbr-deal-finder requires Python3.13 or higher
61
- 2. Optional: Install and use virtualenv
62
- 3. Open your Terminal/Command Prompt
63
- 4. Run `pip3.13 install tbr-deal-finder`
64
-
65
- ### UV
66
- 1. Clone the repository:
67
- ```sh
68
- git clone https://github.com/yourusername/tbr-deal-finder.git
69
- cd tbr-deal-finder
70
- ```
71
- 2. Install uv:
72
- https://docs.astral.sh/uv/getting-started/installation/
73
-
74
62
  ## Configuration
75
63
  This tool can use the csv generated by the app you use to track your TBRs.
76
64
  Here are the steps to get your export.
@@ -103,70 +91,13 @@ If you've got your own CSV you're using to track your TBRs all you need are the
103
91
  Optionally, you can add the `Read Status` column. Set `to-read` for all books you want to be tracked.
104
92
  If you don't add this column the deal finder will run on ALL books in the CSV.
105
93
 
106
- ### tbr-deal-finder setup
107
-
108
- #### Python
109
- ```sh
110
- tbr-deal-finder setup
111
- ```
112
-
113
- #### UV
114
- ```sh
115
- uv run -m tbr_deal_finder.cli setup
116
- ```
117
-
118
- You will be prompted to:
119
- - Enter the path(s) to your StoryGraph export CSV file(s)
120
- - Select your locale (country/region)
121
- - Set your maximum price for deals
122
- - Set your minimum discount percentage
123
-
124
- The configuration will be saved for future runs.
125
-
126
- ## Usage
127
- All commands are available via the CLI:
128
-
129
- - `setup` – Set up or update your configuration interactively.
130
- - `latest-deals` – Find and print the latest book deals based on your config.
131
- - `active-deals` – Show all currently active deals.
132
-
133
- #### Python
134
- ```sh
135
- tbr-deal-finder [COMMAND]
136
- ```
137
-
138
- #### UV
139
- ```sh
140
- uv run -m tbr_deal_finder.cli [COMMAND]
141
- ```
142
-
143
- Example:
144
- ```sh
145
- tbr-deal-finder latest-deals
146
-
147
- # or
148
-
149
- uv run -m tbr_deal_finder.cli latest-deals
150
- ```
151
-
152
- ## Updating your TBR
153
- To update tbr-deal-finder as your TBR changes, regenerate and download your library export.
154
- See [Configuration](#Configuration) for steps.
155
-
156
-
157
- ## Updating the tbr-deal-finder
158
-
159
- ### Python
160
- ```sh
161
- pip3.13 install tbr-deal-finder --upgrade
162
- ```
163
-
164
- ### UV
165
- ```sh
166
- # From the repo directory
167
- git checkout main && git fetch
168
- ```
94
+ ## Installation Guide
95
+ Each guide contains everything you need: installation, usage, and updating instructions.
169
96
 
97
+ ### Choose Your Guide
98
+ - [📱 **Desktop App Guide**](docs/desktop-app.md) - For users who prefer graphical interfaces
99
+ - [🐍 **Python CLI Guide**](docs/python-cli.md) - For command-line and automation users
100
+ - [🛠️ **Development Guide**](docs/development.md) - For developers and contributors
170
101
 
171
102
  ---
172
103
 
@@ -0,0 +1,38 @@
1
+ tbr_deal_finder/__init__.py,sha256=3Zo2T2Th8AT8qexLKWZhNJIeyXPVf2N76kkDGJw5Wc0,104
2
+ tbr_deal_finder/__main__.py,sha256=b2-3WiGIno_XVusUDijQXa2MthKahFz6CVH-hIBe7es,165
3
+ tbr_deal_finder/book.py,sha256=WbvDEeI923iQX_eIsC9H7y-qKVPkfCqwjBCqChssFCM,6740
4
+ tbr_deal_finder/cli.py,sha256=16vZnWS9TTWPhIjZsjsf7OY7Btb0ULOfws5EkUefbRY,7089
5
+ tbr_deal_finder/config.py,sha256=pocHMEoJVIoiuRzEtKDAmsGmiiLgD-lN6n6TU9gI8Lc,3982
6
+ tbr_deal_finder/desktop_updater.py,sha256=VCD4MW1ckx6yyq0N_CJrOth8wePqPhTjhvdJTKsUVkw,5123
7
+ tbr_deal_finder/migrations.py,sha256=fO7r2JbWb6YG0CsPqauakwvbKaEFPxqX1PP8c8N03Wc,4951
8
+ tbr_deal_finder/owned_books.py,sha256=Cf1VeiSg7XBi_TXptJfy5sO1mEgMMQWbJ_P6SzAx0nQ,516
9
+ tbr_deal_finder/retailer_deal.py,sha256=l2n-79kSNZfPh73PZBbSE6tkNYYCaHPEehIPcelDPeY,7214
10
+ tbr_deal_finder/tracked_books.py,sha256=u3KfBNlwvsEwTfM5TAJVLbiTmm1lTe2k70JJOszQz1k,12714
11
+ tbr_deal_finder/utils.py,sha256=LJYRNPRO3XBFIlodGzxCxlxiQ9viNYGU5QjNnjz4qMA,3635
12
+ tbr_deal_finder/version_check.py,sha256=tzuKWngnjSdjPAOdyuPk2ym6Lv3LyeObJAfgsBIH9YQ,1217
13
+ tbr_deal_finder/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ tbr_deal_finder/gui/main.py,sha256=VvdCdSH3mbVhw_3PaDD78U8RQV-drTyXyCNSWFwmoe8,27539
15
+ tbr_deal_finder/gui/pages/__init__.py,sha256=1xc9Ib3oX_hqxLdhZvbCv3AhwOmVBSklZZC6CAcvoMU,20
16
+ tbr_deal_finder/gui/pages/all_books.py,sha256=uQguQ9_NjM5kq_D5DbOWFB9om1lOdPk1iTcjH0yl9bg,3655
17
+ tbr_deal_finder/gui/pages/all_deals.py,sha256=rY08w4XXFc_Jbd39BPJ6fO00vstuuu2YHvZterHx5ZI,2389
18
+ tbr_deal_finder/gui/pages/base_book_page.py,sha256=kmHjJE4eIGrMhXtBWePn_eXfO6qJ36kIoQ7hE1xB3ic,10465
19
+ tbr_deal_finder/gui/pages/book_details.py,sha256=POknUa9yNjfqhC6eXuw7RtaRcFtQj_CdvJ8mK2r6DDo,22047
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
+ tbr_deal_finder/queries/get_active_deals.sql,sha256=nh0F1lRV6YVrUV7gsQpjsgfXmN9R0peBeMHRifjgpUM,212
23
+ tbr_deal_finder/queries/get_deals_found_at.sql,sha256=KqrtQk7FS4Hf74RyL1r-oD2D-RJz1urrxKxkwlvjAro,139
24
+ tbr_deal_finder/queries/latest_deal_last_ran_most_recent_success.sql,sha256=W4cNMAHtcW2DzQyPL8SHHFcbVZQKVK2VfTzazxC3LJU,107
25
+ tbr_deal_finder/queries/latest_unknown_book_sync.sql,sha256=d4ewoYP5otnCj0_TqsXCCLI8BEmHzqTyJrGxTvl2l-I,108
26
+ tbr_deal_finder/retailer/__init__.py,sha256=OD6jUYV8LaURxqHnZq-aiFi7OdWG6qWznRlF_g246lo,316
27
+ tbr_deal_finder/retailer/amazon.py,sha256=oclBGn2jMEaS9J3g6YdcCW4YBDftJWn9-e2f-iXZkvo,4267
28
+ tbr_deal_finder/retailer/amazon_custom_auth.py,sha256=PpPnIjH8iywOFj49QmdnSRy7p_l533FcahM0pdi9mTg,2184
29
+ tbr_deal_finder/retailer/audible.py,sha256=xnxwbaUtpviS3i-2lMdgy_segUKLOuqOwVVbItind5o,4029
30
+ tbr_deal_finder/retailer/chirp.py,sha256=GR8yeXp-1DHMktepy5TecHslrUibpLM7LfueIuTraic,10642
31
+ tbr_deal_finder/retailer/kindle.py,sha256=kA4SO2kl2SvJHADkBYyYMgq_bditStbiTiW7piQPAFI,5282
32
+ tbr_deal_finder/retailer/librofm.py,sha256=dn1kaJEQ9g_AOAFNKhUtFLxYxQZa6RAHgXhT_dx-O3k,7514
33
+ tbr_deal_finder/retailer/models.py,sha256=56xTwcLcw3bFcvTDOb85TktqtksvvyY95hZBbp9-5mY,3340
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,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  tbr-deal-finder = tbr_deal_finder.cli:cli
3
+ tbr-deal-finder-gui = tbr_deal_finder.gui.main:main
@@ -1,24 +0,0 @@
1
- tbr_deal_finder/__init__.py,sha256=WCoj0GZrRiCQlrpkLTw1VUeJmX-RtBLdLqnFYn1Es_4,208
2
- tbr_deal_finder/book.py,sha256=vCvkjU98mI0Z7WW_Z3GppnI4aem9ht-flB8HB4RCujQ,6107
3
- tbr_deal_finder/cli.py,sha256=C4F2rbPrfYNqlmolx08ZHDCcFJuiPbkc4ECXUO25kmI,7446
4
- tbr_deal_finder/config.py,sha256=-TtZLv4kVBf56xPkgAdKXeVRV0qw8MZ53XHBQ1HnVX8,3978
5
- tbr_deal_finder/migrations.py,sha256=_ZxUXzGyEFYlPlpzMvViDVPZJc5BNOiixj150U8HRFc,4224
6
- tbr_deal_finder/owned_books.py,sha256=Cf1VeiSg7XBi_TXptJfy5sO1mEgMMQWbJ_P6SzAx0nQ,516
7
- tbr_deal_finder/retailer_deal.py,sha256=jv32WSOtxVxCkxTCLkOqSkcHGHhWfbD4nSxY42Cqk38,6422
8
- tbr_deal_finder/tracked_books.py,sha256=TdVD5kIgdkDoQNwBIsOikrvFiQiIezmSYf64F5cBN0o,10856
9
- tbr_deal_finder/utils.py,sha256=_4wdGFDtqCdMyoMnwTDiHgCR4WQLAcQr8LlZZZUcq6E,1357
10
- tbr_deal_finder/queries/get_active_deals.sql,sha256=nh0F1lRV6YVrUV7gsQpjsgfXmN9R0peBeMHRifjgpUM,212
11
- tbr_deal_finder/queries/get_deals_found_at.sql,sha256=KqrtQk7FS4Hf74RyL1r-oD2D-RJz1urrxKxkwlvjAro,139
12
- tbr_deal_finder/queries/latest_deal_last_ran_most_recent_success.sql,sha256=W4cNMAHtcW2DzQyPL8SHHFcbVZQKVK2VfTzazxC3LJU,107
13
- tbr_deal_finder/retailer/__init__.py,sha256=OD6jUYV8LaURxqHnZq-aiFi7OdWG6qWznRlF_g246lo,316
14
- tbr_deal_finder/retailer/amazon.py,sha256=rYJlBT4JsOg7QVIhJ8a7xJKUjczP_RMK2m-ddH7FjlQ,2472
15
- tbr_deal_finder/retailer/audible.py,sha256=qwXDKc1W8vGGhqvU2YI7hNfD1rHz2yL-8foXstxb8t8,3991
16
- tbr_deal_finder/retailer/chirp.py,sha256=f_O-6X9duR_gBT8UWDxDr-KQYSRFOOiYOX9Az2pCi6Y,9183
17
- tbr_deal_finder/retailer/kindle.py,sha256=ELdKSzKMCmZWw8TjGHihuyZcgqwVygi94mZFOYQ61qY,4489
18
- tbr_deal_finder/retailer/librofm.py,sha256=Oi9UlkyCqdI-PSRApGSCv_TWP7yWwvYEADOZleAOSmM,6357
19
- tbr_deal_finder/retailer/models.py,sha256=xm99ngt_Ze7yyEwttddkpwL7xjy0YfcFAduV6Rsx63M,2510
20
- tbr_deal_finder-0.2.0.dist-info/METADATA,sha256=ZS-Q4WHHC3Y7BGbSC7j6ClKizcdVMSVfHJ2KpbV7DXk,4363
21
- tbr_deal_finder-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- tbr_deal_finder-0.2.0.dist-info/entry_points.txt,sha256=y_KG1k8xVCY8gngSZ-na2bkK-tTLUdOc_qZ9Djwldv0,60
23
- tbr_deal_finder-0.2.0.dist-info/licenses/LICENSE,sha256=rNc0wNPn4d4HHu6ZheJzeUaz_FbJ4rj2Dr2FjAivkNg,1064
24
- tbr_deal_finder-0.2.0.dist-info/RECORD,,