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.
Files changed (37) hide show
  1. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/CHANGELOG.md +11 -0
  2. {amazon_orders-2.0.2/amazon_orders.egg-info → amazon_orders-2.0.3}/PKG-INFO +1 -1
  3. {amazon_orders-2.0.2 → amazon_orders-2.0.3/amazon_orders.egg-info}/PKG-INFO +1 -1
  4. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/__init__.py +1 -1
  5. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/constants.py +6 -0
  6. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/item.py +6 -5
  7. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/order.py +6 -6
  8. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/parsable.py +24 -0
  9. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/selectors.py +6 -2
  10. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/util.py +3 -0
  11. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/LICENSE +0 -0
  12. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/MANIFEST.in +0 -0
  13. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/README.md +0 -0
  14. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/SOURCES.txt +0 -0
  15. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/dependency_links.txt +0 -0
  16. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/entry_points.txt +0 -0
  17. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/requires.txt +0 -0
  18. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazon_orders.egg-info/top_level.txt +0 -0
  19. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/banner.txt +0 -0
  20. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/cli.py +0 -0
  21. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/conf.py +0 -0
  22. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/__init__.py +0 -0
  23. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/recipient.py +0 -0
  24. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/seller.py +0 -0
  25. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/entity/shipment.py +0 -0
  26. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/exception.py +0 -0
  27. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/forms.py +0 -0
  28. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/orders.py +0 -0
  29. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/amazonorders/session.py +0 -0
  30. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/pyproject.toml +0 -0
  31. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/setup.cfg +0 -0
  32. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_cli.py +0 -0
  33. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_conf.py +0 -0
  34. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_orders.py +0 -0
  35. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_session.py +0 -0
  36. {amazon_orders-2.0.2 → amazon_orders-2.0.3}/tests/test_util.py +0 -0
  37. {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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amazon-orders
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: A CLI and library for interacting with Amazon order history.
5
5
  Maintainer-email: Alex Laird <contact@alexlaird.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amazon-orders
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: A CLI and library for interacting with Amazon order history.
5
5
  Maintainer-email: Alex Laird <contact@alexlaird.com>
6
6
  License: MIT License
@@ -1,3 +1,3 @@
1
1
  __copyright__ = "Copyright (c) 2024 Alex Laird"
2
2
  __license__ = "MIT"
3
- __version__ = "2.0.2"
3
+ __version__ = "2.0.3"
@@ -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, datetime
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.FIELD_ITEM_TAG_ITERATOR_SELECTOR,
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.FIELD_ITEM_TAG_ITERATOR_SELECTOR,
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.FIELD_ITEM_TAG_ITERATOR_SELECTOR):
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 = datetime.strptime(date_str, "%b %d, %Y").date()
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, datetime
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
- value = value.split(split_str)[1].strip()
141
- value = datetime.strptime(value, "%B %d, %Y").date()
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 = datetime.strptime(date_str, "%B %d, %Y").date()
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 = datetime.strptime(date_str, "%B %d, %Y").date()
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", "[data-component='shipments']"]
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", "[data-component='purchasedItemsRightGrid']"]
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