Skip to content

Commit c3d4108

Browse files
committed
dependency updates, updated localization
1 parent 07f5898 commit c3d4108

11 files changed

+481
-440
lines changed

.devcontainer/devcontainer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"VARIANT": "3.11",
1313
// Options
1414
// "NODE_VERSION": "lts/*",
15-
"POETRY_VERSION": "1.8.2"
15+
"POETRY_VERSION": "1.8.3"
1616
}
1717
},
1818
// Set *default* container specific settings.json values on container create.

.pre-commit-config.yaml

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ repos:
1313
- id: check-added-large-files
1414

1515
- repo: https://github.com/ambv/black
16-
rev: 24.4.2
16+
rev: 24.8.0
1717
hooks:
1818
- id: black
1919
args: [--line-length, '130', --target-version, py38]
@@ -25,13 +25,13 @@ repos:
2525
args: [--profile, black]
2626

2727
- repo: https://github.com/PyCQA/flake8
28-
rev: 7.1.0
28+
rev: 7.1.1
2929
hooks:
3030
- id: flake8
3131
args: [--max-line-length, '130']
3232

3333
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
34-
rev: v2.13.0
34+
rev: v2.14.0
3535
hooks:
3636
- id: pretty-format-toml
3737
args: [--autofix]
@@ -42,13 +42,13 @@ repos:
4242
args: [--autofix]
4343

4444
- repo: https://github.com/igorshubovych/markdownlint-cli
45-
rev: v0.41.0
45+
rev: v0.42.0
4646
hooks:
4747
- id: markdownlint
4848
args: [--fix]
4949

5050
- repo: https://github.com/python-poetry/poetry
51-
rev: 1.8.2
51+
rev: 1.8.3
5252
hooks:
5353
- id: poetry-check
5454
- id: poetry-lock
@@ -57,7 +57,7 @@ repos:
5757
args: [-f, requirements.txt, -o, requirements.txt, --without-hashes]
5858

5959
- repo: https://github.com/pre-commit/mirrors-mypy
60-
rev: v1.10.1
60+
rev: v1.11.2
6161
hooks:
6262
- id: mypy
6363
additional_dependencies: [types-requests]

poetry.lock

+386-347
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ name = "tgtg-scanner"
1212
packages = [{include = "tgtg_scanner"}]
1313
readme = "README.md"
1414
repository = "https://github.com/Der-Henning/tgtg"
15-
version = "1.21.2"
15+
version = "1.22.0"
1616

1717
[tool.poetry.dependencies]
1818
apprise = "^1.4.0"
@@ -24,7 +24,7 @@ googlemaps = "^4.10.0"
2424
humanize = "^4.7.0"
2525
packaging = "^24.0"
2626
progress = "^1.6"
27-
prometheus-client = "^0.20.0"
27+
prometheus-client = "^0.21.0"
2828
pycron = "^3.0.0"
2929
python = ">=3.9,<3.13"
3030
python-pushsafer = "^1.1"

requirements.txt

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
1-
aiohappyeyeballs==2.3.7 ; python_version >= "3.9" and python_version < "3.13"
2-
aiohttp==3.10.4 ; python_version >= "3.9" and python_version < "3.13"
1+
aiohappyeyeballs==2.4.2 ; python_version >= "3.9" and python_version < "3.13"
2+
aiohttp==3.10.8 ; python_version >= "3.9" and python_version < "3.13"
33
aiosignal==1.3.1 ; python_version >= "3.9" and python_version < "3.13"
4-
anyio==4.4.0 ; python_version >= "3.9" and python_version < "3.13"
5-
apprise==1.8.1 ; python_version >= "3.9" and python_version < "3.13"
4+
anyio==4.6.0 ; python_version >= "3.9" and python_version < "3.13"
5+
apprise==1.9.0 ; python_version >= "3.9" and python_version < "3.13"
66
async-timeout==4.0.3 ; python_version >= "3.9" and python_version < "3.11"
77
attrs==24.2.0 ; python_version >= "3.9" and python_version < "3.13"
88
babel==2.16.0 ; python_version >= "3.9" and python_version < "3.13"
9-
cachetools==5.3.3 ; python_version >= "3.9" and python_version < "3.13"
10-
certifi==2024.7.4 ; python_version >= "3.9" and python_version < "3.13"
9+
cachetools==5.5.0 ; python_version >= "3.9" and python_version < "3.13"
10+
certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.13"
1111
charset-normalizer==3.3.2 ; python_version >= "3.9" and python_version < "3.13"
1212
click==8.1.7 ; python_version >= "3.9" and python_version < "3.13"
1313
colorama==0.4.6 ; python_version >= "3.9" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows")
1414
colorlog==6.8.2 ; python_version >= "3.9" and python_version < "3.13"
15-
cron-descriptor==1.4.3 ; python_version >= "3.9" and python_version < "3.13"
15+
cron-descriptor==1.4.5 ; python_version >= "3.9" and python_version < "3.13"
1616
discord-py==2.4.0 ; python_version >= "3.9" and python_version < "3.13"
1717
discord==2.3.2 ; python_version >= "3.9" and python_version < "3.13"
1818
exceptiongroup==1.2.2 ; python_version >= "3.9" and python_version < "3.11"
1919
frozenlist==1.4.1 ; python_version >= "3.9" and python_version < "3.13"
2020
googlemaps==4.10.0 ; python_version >= "3.9" and python_version < "3.13"
2121
h11==0.14.0 ; python_version >= "3.9" and python_version < "3.13"
2222
httpcore==1.0.5 ; python_version >= "3.9" and python_version < "3.13"
23-
httpx==0.27.0 ; python_version >= "3.9" and python_version < "3.13"
23+
httpx==0.27.2 ; python_version >= "3.9" and python_version < "3.13"
2424
humanize==4.10.0 ; python_version >= "3.9" and python_version < "3.13"
25-
idna==3.7 ; python_version >= "3.9" and python_version < "3.13"
26-
importlib-metadata==8.2.0 ; python_version >= "3.9" and python_version < "3.10"
25+
idna==3.10 ; python_version >= "3.9" and python_version < "3.13"
26+
importlib-metadata==8.5.0 ; python_version >= "3.9" and python_version < "3.10"
2727
markdown==3.7 ; python_version >= "3.9" and python_version < "3.13"
28-
multidict==6.0.5 ; python_version >= "3.9" and python_version < "3.13"
28+
multidict==6.1.0 ; python_version >= "3.9" and python_version < "3.13"
2929
oauthlib==3.2.2 ; python_version >= "3.9" and python_version < "3.13"
3030
packaging==24.1 ; python_version >= "3.9" and python_version < "3.13"
3131
progress==1.6 ; python_version >= "3.9" and python_version < "3.13"
32-
prometheus-client==0.20.0 ; python_version >= "3.9" and python_version < "3.13"
33-
pycron==3.0.0 ; python_version >= "3.9" and python_version < "3.13"
32+
prometheus-client==0.21.0 ; python_version >= "3.9" and python_version < "3.13"
33+
pycron==3.1.1 ; python_version >= "3.9" and python_version < "3.13"
3434
python-pushsafer==1.1 ; python_version >= "3.9" and python_version < "3.13"
35-
python-telegram-bot[callback-data]==21.4 ; python_version >= "3.9" and python_version < "3.13"
35+
python-telegram-bot[callback-data]==21.6 ; python_version >= "3.9" and python_version < "3.13"
3636
pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.13"
3737
requests-oauthlib==2.0.0 ; python_version >= "3.9" and python_version < "3.13"
3838
requests==2.32.3 ; python_version >= "3.9" and python_version < "3.13"
3939
sniffio==1.3.1 ; python_version >= "3.9" and python_version < "3.13"
4040
typing-extensions==4.12.2 ; python_version >= "3.9" and python_version < "3.11"
41-
urllib3==2.2.2 ; python_version >= "3.9" and python_version < "3.13"
42-
yarl==1.9.4 ; python_version >= "3.9" and python_version < "3.13"
43-
zipp==3.20.0 ; python_version >= "3.9" and python_version < "3.10"
41+
urllib3==2.2.3 ; python_version >= "3.9" and python_version < "3.13"
42+
yarl==1.13.1 ; python_version >= "3.9" and python_version < "3.13"
43+
zipp==3.20.2 ; python_version >= "3.9" and python_version < "3.10"

tests/test_item.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def test_item(tgtg_item: dict, monkeypatch: pytest.MonkeyPatch):
1717
assert item.item_category == tgtg_item.get("item", {}).get("item_category", "-")
1818
assert item.description == tgtg_item.get("item", {}).get("description", "-")
1919
assert item.link == "https://share.toogoodtogo.com/item/774625"
20-
assert item.price == "3.00"
21-
assert item.value == "9.00"
20+
assert item.price == "3.00"
21+
assert item.value == "9.00"
2222
assert item.currency == "EUR"
2323
assert item.store_name == tgtg_item.get("store", {}).get("store_name", "-")
2424
assert item.item_logo == tgtg_item.get("item", {}).get("logo_picture", {}).get("current_url", "-")

tgtg_scanner/models/item.py

+59-57
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import requests
1010

1111
from tgtg_scanner.errors import MaskConfigurationError
12-
from tgtg_scanner.models import Config
1312
from tgtg_scanner.models.location import DistanceTime, Location
1413

1514
ATTRS = [
@@ -19,13 +18,10 @@
1918
"description",
2019
"price",
2120
"value",
22-
"price_localized",
23-
"value_localized",
2421
"currency",
2522
"pickupdate",
2623
"favorite",
2724
"rating",
28-
"rating_localized",
2925
"buffet",
3026
"item_category",
3127
"item_name",
@@ -57,58 +53,64 @@ class Item:
5753
returns well formated data for notifications.
5854
"""
5955

60-
def __init__(self, data: dict, config: Union[Config, None] = None, location: Union[Location, None] = None):
61-
self.items_available = data.get("items_available", 0)
62-
self.display_name = data.get("display_name", "-")
63-
self.favorite = "Yes" if data.get("favorite", False) else "No"
64-
self.pickup_interval_start = data.get("pickup_interval", {}).get("start", None)
65-
self.pickup_interval_end = data.get("pickup_interval", {}).get("end", None)
66-
self.pickup_location = data.get("pickup_location", {}).get("address", {}).get("address_line", "-")
67-
68-
item = data.get("item", {})
69-
self.item_id = item.get("item_id")
70-
self.rating = item.get("average_overall_rating", {}).get("average_overall_rating", None)
71-
self.rating = "-" if not self.rating else f"{self.rating:.1f}"
72-
self.packaging_option = item.get("packaging_option", "-")
73-
self.item_name = item.get("name", "-")
74-
self.buffet = "Yes" if item.get("buffet", False) else "No"
75-
self.item_category = item.get("item_category", "-")
76-
self.description = item.get("description", "-")
77-
item_price = item.get("item_price", {})
78-
item_value = item.get("item_value", {})
79-
price = item_price.get("minor_units", 0) / 10 ** item_price.get("decimals", 0)
80-
value = item_value.get("minor_units", 0) / 10 ** item_value.get("decimals", 0)
81-
self.price = f"{price:.2f}"
82-
self.value = f"{value:.2f}"
83-
self.currency = item_price.get("code", "-")
84-
self.item_logo = item.get("logo_picture", {}).get(
56+
def __init__(self, data: dict, location: Union[Location, None] = None, locale: str = "en_US"):
57+
self.items_available: int = data.get("items_available", 0)
58+
self.display_name: str = data.get("display_name", "-")
59+
self.favorite: str = "Yes" if data.get("favorite", False) else "No"
60+
self.pickup_interval_start: Union[str, None] = data.get("pickup_interval", {}).get("start", None)
61+
self.pickup_interval_end: Union[str, None] = data.get("pickup_interval", {}).get("end", None)
62+
self.pickup_location: str = data.get("pickup_location", {}).get("address", {}).get("address_line", "-")
63+
64+
item: dict = data.get("item", {})
65+
self.item_id: str = item.get("item_id", None)
66+
self._rating: Union[float, None] = item.get("average_overall_rating", {}).get("average_overall_rating", None)
67+
self.packaging_option: str = item.get("packaging_option", "-")
68+
self.item_name: str = item.get("name", "-")
69+
self.buffet: str = "Yes" if item.get("buffet", False) else "No"
70+
self.item_category: str = item.get("item_category", "-")
71+
self.description: str = item.get("description", "-")
72+
item_price: dict = item.get("item_price", {})
73+
item_value: dict = item.get("item_value", {})
74+
self._price: float = item_price.get("minor_units", 0) / 10 ** item_price.get("decimals", 0)
75+
self._value: float = item_value.get("minor_units", 0) / 10 ** item_value.get("decimals", 0)
76+
self.currency: str = item_price.get("code", "-")
77+
self.item_logo: str = item.get("logo_picture", {}).get(
8578
"current_url",
8679
"https://tgtg-mkt-cms-prod.s3.eu-west-1.amazonaws.com/13512/TGTG_Icon_White_Cirle_1988x1988px_RGB.png",
8780
)
88-
self.item_cover = item.get("cover_picture", {}).get(
81+
self.item_cover: str = item.get("cover_picture", {}).get(
8982
"current_url",
9083
"https://images.tgtg.ninja/standard_images/GENERAL/other1.jpg",
9184
)
9285

93-
store = data.get("store", {})
94-
self.store_name = store.get("store_name", "-")
86+
store: dict = data.get("store", {})
87+
self.store_name: str = store.get("store_name", "-")
9588

96-
self.scanned_on = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
89+
self.scanned_on: str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
9790
self.location = location
91+
self.locale = locale
9892

99-
# Localize attributes, first init with decoded values
100-
self.price_localized = self.price
101-
self.rating_localized = self.rating
102-
self.value_localized = self.value
103-
if config:
104-
try:
105-
self.rating_localized = babel.numbers.format_decimal(self.rating, locale=config.locale)
93+
@property
94+
def rating(self) -> str:
95+
if self._rating is None:
96+
return "-"
97+
return self._format_decimal(round(self._rating, 1))
98+
99+
@property
100+
def price(self) -> str:
101+
return self._format_currency(self._price)
102+
103+
@property
104+
def value(self) -> str:
105+
return self._format_currency(self._value)
106+
107+
def _format_decimal(self, number: float) -> str:
108+
return babel.numbers.format_decimal(number, locale=self.locale)
106109

107-
if self.currency != "-":
108-
self.price_localized = babel.numbers.format_currency(self.price, self.currency, locale=config.locale)
109-
self.value_localized = babel.numbers.format_currency(self.value, self.currency, locale=config.locale)
110-
except babel.numbers.NumberFormatError:
111-
pass
110+
def _format_currency(self, number: float) -> str:
111+
if self.currency == "-":
112+
return self._format_decimal(number)
113+
return babel.numbers.format_currency(number, self.currency, locale=self.locale)
112114

113115
@staticmethod
114116
def _datetimeparse(datestr: str) -> datetime.datetime:
@@ -174,18 +176,18 @@ def pickupdate(self) -> str:
174176
"""
175177
Returns a well formated string, providing the pickup time range
176178
"""
177-
if self.pickup_interval_start and self.pickup_interval_end:
178-
now = datetime.datetime.now()
179-
pfr = self._datetimeparse(self.pickup_interval_start)
180-
pto = self._datetimeparse(self.pickup_interval_end)
181-
prange = f"{pfr.hour:02d}:{pfr.minute:02d} - {pto.hour:02d}:{pto.minute:02d}"
182-
tommorow = now + datetime.timedelta(days=1)
183-
if now.date() == pfr.date():
184-
return f"{humanize.naturalday(now)}, {prange}"
185-
if (pfr.date() - now.date()).days == 1:
186-
return f"{humanize.naturalday(tommorow)}, {prange}"
187-
return f"{pfr.day}/{pfr.month}, {prange}"
188-
return "-"
179+
if self.pickup_interval_start is None or self.pickup_interval_end is None:
180+
return "-"
181+
now = datetime.datetime.now()
182+
pfr = self._datetimeparse(self.pickup_interval_start)
183+
pto = self._datetimeparse(self.pickup_interval_end)
184+
prange = f"{pfr.hour:02d}:{pfr.minute:02d} - {pto.hour:02d}:{pto.minute:02d}"
185+
tommorow = now + datetime.timedelta(days=1)
186+
if now.date() == pfr.date():
187+
return f"{humanize.naturalday(now)}, {prange}"
188+
if (pfr.date() - now.date()).days == 1:
189+
return f"{humanize.naturalday(tommorow)}, {prange}"
190+
return f"{pfr.day}/{pfr.month}, {prange}"
189191

190192
def _get_distance_time(self, travel_mode: str) -> Union[DistanceTime, None]:
191193
if self.location is None:

tgtg_scanner/models/metrics.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def update(self, item: Item) -> None:
4040
"""
4141
try:
4242
self.item_count.labels(item.item_id, item.display_name).set(item.items_available)
43-
self.item_price.labels(item.item_id, item.display_name).set(float(item.price))
44-
self.item_value.labels(item.item_id, item.display_name).set(float(item.value))
43+
self.item_price.labels(item.item_id, item.display_name).set(item._price)
44+
self.item_value.labels(item.item_id, item.display_name).set(item._value)
4545
except ValueError as err:
4646
log.warning("Error updating metrics: %s", err)

tgtg_scanner/notifiers/discord.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ async def _listen_for_items(self):
6969
item = self.queue.get(block=False)
7070
if item is None:
7171
self.bot.dispatch("close")
72+
await self.bot.close()
7273
return
7374
log.debug("Sending %s Notification", self.name)
7475
await self._send(item)
@@ -89,7 +90,6 @@ def _run(self):
8990
# Commands are handled separately, in case commands are not enabled
9091
self._setup_commands()
9192
asyncio.run(self.bot.start(self.token))
92-
self.bot.http.connector.close()
9393

9494
def _setup_events(self):
9595
@self.bot.event

tgtg_scanner/notifiers/smtp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def _stay_connected(self) -> None:
9494
if status != 250:
9595
self._connect()
9696

97-
def _send_mail(self, subject: str, html: str, item_id: int) -> None:
97+
def _send_mail(self, subject: str, html: str, item_id: str) -> None:
9898
"""Sends mail with html body"""
9999
if self.server is None:
100100
self._connect()
@@ -105,7 +105,7 @@ def _send_mail(self, subject: str, html: str, item_id: int) -> None:
105105

106106
# Contains either the main recipient(s) or recipient(s) that should be
107107
# notified for the specific item. First, initalize with main recipient(s)
108-
recipients = self.item_recipients.get(str(item_id), self.recipients)
108+
recipients = self.item_recipients.get(item_id, self.recipients)
109109

110110
message["To"] = ", ".join(recipients)
111111
message["Subject"] = subject

tgtg_scanner/scanner.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _get_test_item(self) -> Item:
7979
return items[0]
8080
items = sorted(
8181
[
82-
Item(item, self.config, self.location)
82+
Item(item, self.location, self.config.locale)
8383
for item in self.tgtg_client.get_items(favorites_only=False, latitude=53.5511, longitude=9.9937, radius=50)
8484
],
8585
key=lambda x: x.items_available,
@@ -100,7 +100,7 @@ def _job(self) -> None:
100100
try:
101101
if item_id != "":
102102
item_dict = self.tgtg_client.get_item(item_id)
103-
items.append(Item(item_dict, self.config, self.location))
103+
items.append(Item(item_dict, self.location, self.config.locale))
104104
except TgtgAPIError as err:
105105
log.error(err)
106106
items += self._get_favorites()
@@ -133,7 +133,7 @@ def _get_favorites(self) -> list[Item]:
133133
except TgtgAPIError as err:
134134
log.error(err)
135135
return []
136-
return [Item(item, self.config, self.location) for item in items]
136+
return [Item(item, self.location, self.config.locale) for item in items]
137137

138138
def _check_item(self, item: Item) -> None:
139139
"""

0 commit comments

Comments
 (0)