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.
- tbr_deal_finder/__init__.py +1 -5
- tbr_deal_finder/__main__.py +7 -0
- tbr_deal_finder/book.py +28 -8
- tbr_deal_finder/cli.py +15 -28
- tbr_deal_finder/config.py +3 -3
- tbr_deal_finder/desktop_updater.py +147 -0
- tbr_deal_finder/gui/__init__.py +0 -0
- tbr_deal_finder/gui/main.py +754 -0
- tbr_deal_finder/gui/pages/__init__.py +1 -0
- tbr_deal_finder/gui/pages/all_books.py +100 -0
- tbr_deal_finder/gui/pages/all_deals.py +63 -0
- tbr_deal_finder/gui/pages/base_book_page.py +290 -0
- tbr_deal_finder/gui/pages/book_details.py +604 -0
- tbr_deal_finder/gui/pages/latest_deals.py +376 -0
- tbr_deal_finder/gui/pages/settings.py +390 -0
- tbr_deal_finder/migrations.py +26 -0
- tbr_deal_finder/queries/latest_unknown_book_sync.sql +5 -0
- tbr_deal_finder/retailer/amazon.py +60 -9
- tbr_deal_finder/retailer/amazon_custom_auth.py +79 -0
- tbr_deal_finder/retailer/audible.py +2 -1
- tbr_deal_finder/retailer/chirp.py +55 -11
- tbr_deal_finder/retailer/kindle.py +68 -44
- tbr_deal_finder/retailer/librofm.py +58 -21
- tbr_deal_finder/retailer/models.py +31 -1
- tbr_deal_finder/retailer_deal.py +69 -37
- tbr_deal_finder/tracked_books.py +76 -8
- tbr_deal_finder/utils.py +74 -3
- tbr_deal_finder/version_check.py +40 -0
- {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/METADATA +19 -88
- tbr_deal_finder-0.3.2.dist-info/RECORD +38 -0
- {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/entry_points.txt +1 -0
- tbr_deal_finder-0.2.0.dist-info/RECORD +0 -24
- {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/WHEEL +0 -0
- {tbr_deal_finder-0.2.0.dist-info → tbr_deal_finder-0.3.2.dist-info}/licenses/LICENSE +0 -0
tbr_deal_finder/tracked_books.py
CHANGED
@@ -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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
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(
|
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
|
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
|
-
|
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
|
26
|
-
- Tracks deals on the wishlist of all your configured retailers like audible
|
27
|
-
- Supports multiple locales
|
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
|
-
|
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,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,,
|
File without changes
|
File without changes
|