amazon-orders 2.0.2__tar.gz → 2.0.3__tar.gz
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.
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/CHANGELOG.md +11 -0
- {amazon_orders-2.0.2/amazon_orders.egg-info → amazon_orders-2.0.3}/PKG-INFO +1 -1
- {amazon_orders-2.0.2 → amazon_orders-2.0.3/amazon_orders.egg-info}/PKG-INFO +1 -1
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/__init__.py +1 -1
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/constants.py +6 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/item.py +6 -5
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/order.py +6 -6
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/parsable.py +24 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/selectors.py +6 -2
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/util.py +3 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/LICENSE +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/MANIFEST.in +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/README.md +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/banner.txt +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/cli.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/conf.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/exception.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/forms.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/orders.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/session.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/pyproject.toml +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/setup.cfg +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_cli.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_conf.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_orders.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_session.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_util.py +0 -0
- {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/testcase.py +0 -0
|
@@ -6,6 +6,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/2.0.2...HEAD)
|
|
8
8
|
|
|
9
|
+
## [2.0.3](https://github.com/alexdlaird/amazon-orders/compare/2.0.2...2.0.3) - 2024-11-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Further support for Amazon's new `data-component` tag on order price, seller, and return eligibility, and fixing an issue with `Shipment` parsing.
|
|
14
|
+
- [`Parsable.to_date()`](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.parsable.Parsable.to_date) attempts multiple date formats.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- An issue with `Shipment`s parsing with Amazon's new `data-component`.
|
|
19
|
+
|
|
9
20
|
## [2.0.2](https://github.com/alexdlaird/amazon-orders/compare/2.0.1...2.0.2) - 2024-10-30
|
|
10
21
|
|
|
11
22
|
### Added
|
|
@@ -53,3 +53,9 @@ class Constants:
|
|
|
53
53
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
54
54
|
"Chrome/120.0.0.0 Safari/537.36",
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
##########################################################################
|
|
58
|
+
# Formats
|
|
59
|
+
##########################################################################
|
|
60
|
+
|
|
61
|
+
VALID_DATE_FORMATS = ["%b %d, %Y", "%B %d, %Y"]
|
|
@@ -2,7 +2,7 @@ __copyright__ = "Copyright (c) 2024 Alex Laird"
|
|
|
2
2
|
__license__ = "MIT"
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
from datetime import date
|
|
5
|
+
from datetime import date
|
|
6
6
|
from typing import Optional, TypeVar
|
|
7
7
|
|
|
8
8
|
from bs4 import Tag
|
|
@@ -35,11 +35,11 @@ class Item(Parsable):
|
|
|
35
35
|
link=True, required=True)
|
|
36
36
|
#: The Item price.
|
|
37
37
|
self.price: Optional[float] = self.to_currency(
|
|
38
|
-
self.safe_simple_parse(selector=self.config.selectors.
|
|
38
|
+
self.safe_simple_parse(selector=self.config.selectors.FIELD_ITEM_PRICE_SELECTOR,
|
|
39
39
|
prefix_split="$"))
|
|
40
40
|
#: The Item Seller.
|
|
41
41
|
self.seller: Optional[Seller] = self.safe_simple_parse(
|
|
42
|
-
selector=self.config.selectors.
|
|
42
|
+
selector=self.config.selectors.FIELD_ITEM_SELLER_SELECTOR,
|
|
43
43
|
text_contains="Sold by:",
|
|
44
44
|
wrap_tag=Seller)
|
|
45
45
|
#: The Item condition.
|
|
@@ -69,7 +69,7 @@ class Item(Parsable):
|
|
|
69
69
|
def _parse_return_eligible_date(self) -> Optional[date]:
|
|
70
70
|
value = None
|
|
71
71
|
|
|
72
|
-
for tag in util.select(self.parsed, self.config.selectors.
|
|
72
|
+
for tag in util.select(self.parsed, self.config.selectors.FIELD_ITEM_RETURN_SELECTOR):
|
|
73
73
|
if "Return" in tag.text:
|
|
74
74
|
tag_str = tag.text.strip()
|
|
75
75
|
split_str = "through "
|
|
@@ -77,6 +77,7 @@ class Item(Parsable):
|
|
|
77
77
|
split_str = "closed on "
|
|
78
78
|
if split_str in tag_str:
|
|
79
79
|
date_str = tag_str.split(split_str)[1]
|
|
80
|
-
value =
|
|
80
|
+
value = self.to_date(date_str)
|
|
81
|
+
break
|
|
81
82
|
|
|
82
83
|
return value
|
|
@@ -3,7 +3,7 @@ __license__ = "MIT"
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
-
from datetime import date
|
|
6
|
+
from datetime import date
|
|
7
7
|
from typing import Any, List, Optional, TypeVar, Union
|
|
8
8
|
|
|
9
9
|
from bs4 import BeautifulSoup, Tag
|
|
@@ -137,8 +137,8 @@ class Order(Parsable):
|
|
|
137
137
|
else:
|
|
138
138
|
split_str = "Order placed"
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
value =
|
|
140
|
+
date_str = value.split(split_str)[1].strip()
|
|
141
|
+
value = self.to_date(date_str)
|
|
142
142
|
|
|
143
143
|
return value
|
|
144
144
|
|
|
@@ -149,7 +149,7 @@ class Order(Parsable):
|
|
|
149
149
|
value = util.select_one(self.parsed, self.config.selectors.FIELD_ORDER_ADDRESS_FALLBACK_1_SELECTOR)
|
|
150
150
|
|
|
151
151
|
if value:
|
|
152
|
-
data_popover = value.get("data-a-popover", {}) # type: ignore[arg-type]
|
|
152
|
+
data_popover = value.get("data-a-popover", {}) # type: ignore[arg-type, var-annotated]
|
|
153
153
|
inline_content = data_popover.get("inlineContent") # type: ignore[union-attr]
|
|
154
154
|
if inline_content:
|
|
155
155
|
value = BeautifulSoup(json.loads(inline_content), "html.parser")
|
|
@@ -272,7 +272,7 @@ class Order(Parsable):
|
|
|
272
272
|
|
|
273
273
|
if value:
|
|
274
274
|
date_str = value.split("-")[0].strip()
|
|
275
|
-
value =
|
|
275
|
+
value = self.to_date(date_str)
|
|
276
276
|
|
|
277
277
|
return value
|
|
278
278
|
|
|
@@ -282,7 +282,7 @@ class Order(Parsable):
|
|
|
282
282
|
|
|
283
283
|
if value:
|
|
284
284
|
date_str = value.split("-")[0].strip()
|
|
285
|
-
value =
|
|
285
|
+
value = self.to_date(date_str)
|
|
286
286
|
|
|
287
287
|
return value
|
|
288
288
|
|
|
@@ -2,6 +2,7 @@ __copyright__ = "Copyright (c) 2024 Alex Laird"
|
|
|
2
2
|
__license__ = "MIT"
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
+
from datetime import date, datetime
|
|
5
6
|
from typing import Any, Callable, Optional, Type, Union, Dict
|
|
6
7
|
|
|
7
8
|
from bs4 import Tag
|
|
@@ -169,3 +170,26 @@ class Parsable:
|
|
|
169
170
|
return None
|
|
170
171
|
|
|
171
172
|
return currency
|
|
173
|
+
|
|
174
|
+
def to_date(self,
|
|
175
|
+
date_str: str) -> Optional[date]:
|
|
176
|
+
"""
|
|
177
|
+
Return the given date string as a date object.
|
|
178
|
+
|
|
179
|
+
:param date_str: The date string to parse to a date object.
|
|
180
|
+
:return: The parsed date.
|
|
181
|
+
"""
|
|
182
|
+
value = None
|
|
183
|
+
|
|
184
|
+
for fmt in self.config.constants.VALID_DATE_FORMATS:
|
|
185
|
+
try:
|
|
186
|
+
value = datetime.strptime(date_str, fmt).date()
|
|
187
|
+
except ValueError:
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
if value is None:
|
|
191
|
+
logger.warning(
|
|
192
|
+
f"ValueError: time data '{date_str}' does not match any format in "
|
|
193
|
+
f"{self.config.constants.VALID_DATE_FORMATS}")
|
|
194
|
+
|
|
195
|
+
return value
|
|
@@ -38,7 +38,8 @@ class Selectors:
|
|
|
38
38
|
ORDER_HISTORY_ENTITY_SELECTOR = ["div.order", "div.order-card"]
|
|
39
39
|
ORDER_DETAILS_ENTITY_SELECTOR = ["div#orderDetails", "div#ordersContainer", "[data-component='orderCard']"]
|
|
40
40
|
ITEM_ENTITY_SELECTOR = ["div:has(> div.yohtmlc-item)", ".item-box", "[data-component='purchasedItems']"]
|
|
41
|
-
SHIPMENT_ENTITY_SELECTOR = ["div.shipment", "div.delivery-box",
|
|
41
|
+
SHIPMENT_ENTITY_SELECTOR = ["div.shipment", "div.delivery-box",
|
|
42
|
+
"[data-component='orderCard'] [data-component='shipments']"]
|
|
42
43
|
|
|
43
44
|
#####################################
|
|
44
45
|
# CSS selectors for Item fields
|
|
@@ -48,7 +49,10 @@ class Selectors:
|
|
|
48
49
|
FIELD_ITEM_QUANTITY_SELECTOR = ["span.item-view-qty", "span.product-image__qty", "[data-component='itemQuantity']"]
|
|
49
50
|
FIELD_ITEM_TITLE_SELECTOR = [".yohtmlc-item a", ".yohtmlc-product-title", "[data-component='itemTitle']"]
|
|
50
51
|
FIELD_ITEM_LINK_SELECTOR = [".yohtmlc-item a", "a:has(> .yohtmlc-product-title)", "[data-component='itemTitle'] a"]
|
|
51
|
-
FIELD_ITEM_TAG_ITERATOR_SELECTOR = [".yohtmlc-item div"
|
|
52
|
+
FIELD_ITEM_TAG_ITERATOR_SELECTOR = [".yohtmlc-item div"]
|
|
53
|
+
FIELD_ITEM_PRICE_SELECTOR = ["[data-component='unitPrice']"] + FIELD_ITEM_TAG_ITERATOR_SELECTOR
|
|
54
|
+
FIELD_ITEM_SELLER_SELECTOR = ["[data-component='orderedMerchantName']"] + FIELD_ITEM_TAG_ITERATOR_SELECTOR
|
|
55
|
+
FIELD_ITEM_RETURN_SELECTOR = ["[data-component='itemReturnEligibility']"] + FIELD_ITEM_TAG_ITERATOR_SELECTOR
|
|
52
56
|
|
|
53
57
|
#####################################
|
|
54
58
|
# CSS selectors for Order fields
|
|
@@ -2,10 +2,13 @@ __copyright__ = "Copyright (c) 2024 Alex Laird"
|
|
|
2
2
|
__license__ = "MIT"
|
|
3
3
|
|
|
4
4
|
import importlib
|
|
5
|
+
import logging
|
|
5
6
|
from typing import List, Union, Optional, Callable
|
|
6
7
|
|
|
7
8
|
from bs4 import Tag
|
|
8
9
|
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
def select(parsed: Tag, selector: Union[List[str], str]) -> List[Tag]:
|
|
11
14
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|