amazon-orders 3.2.3__tar.gz → 3.2.4__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 (40) hide show
  1. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/CHANGELOG.md +14 -3
  2. {amazon_orders-3.2.3/amazon_orders.egg-info → amazon_orders-3.2.4}/PKG-INFO +2 -2
  3. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/README.md +1 -1
  4. {amazon_orders-3.2.3 → amazon_orders-3.2.4/amazon_orders.egg-info}/PKG-INFO +2 -2
  5. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/__init__.py +1 -1
  6. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/order.py +9 -2
  7. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/parsable.py +21 -3
  8. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/orders.py +2 -3
  9. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/selectors.py +12 -3
  10. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_orders.py +24 -0
  11. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/LICENSE +0 -0
  12. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/MANIFEST.in +0 -0
  13. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazon_orders.egg-info/SOURCES.txt +0 -0
  14. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazon_orders.egg-info/dependency_links.txt +0 -0
  15. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazon_orders.egg-info/entry_points.txt +0 -0
  16. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazon_orders.egg-info/requires.txt +0 -0
  17. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazon_orders.egg-info/top_level.txt +0 -0
  18. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/banner.txt +0 -0
  19. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/cli.py +0 -0
  20. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/conf.py +0 -0
  21. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/constants.py +0 -0
  22. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/__init__.py +0 -0
  23. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/item.py +0 -0
  24. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/recipient.py +0 -0
  25. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/seller.py +0 -0
  26. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/shipment.py +0 -0
  27. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/entity/transaction.py +0 -0
  28. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/exception.py +0 -0
  29. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/forms.py +0 -0
  30. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/session.py +0 -0
  31. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/transactions.py +0 -0
  32. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/amazonorders/util.py +0 -0
  33. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/pyproject.toml +0 -0
  34. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/setup.cfg +0 -0
  35. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_cli.py +0 -0
  36. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_conf.py +0 -0
  37. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_session.py +0 -0
  38. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_transactions.py +0 -0
  39. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/test_util.py +0 -0
  40. {amazon_orders-3.2.3 → amazon_orders-3.2.4}/tests/testcase.py +0 -0
@@ -4,9 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.2...HEAD)
7
+ ## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.4...HEAD)
8
8
 
9
- ## [3.2.3](https://github.com/alexdlaird/amazon-orders/compare/3.2.1...3.2.2) - 2025-02-06
9
+ ## [3.2.4](https://github.com/alexdlaird/amazon-orders/compare/3.2.3...3.2.4) - 2025-02-11
10
+
11
+ ### Added
12
+
13
+ - `Order.promotion_applied` field, which is now parsed alongside other subtotals.
14
+
15
+ ### Fixed
16
+
17
+ - Broken parsing of Whole Foods Market orders, these are now skipped.
18
+ - Parsing issue on `Order.order_number` and `Order.order_placed` due to changes in Amazon.com DOM.
19
+
20
+ ## [3.2.3](https://github.com/alexdlaird/amazon-orders/compare/3.2.2...3.2.3) - 2025-02-06
10
21
 
11
22
  ### Added
12
23
 
@@ -263,7 +274,7 @@ Build and stability improvements.
263
274
  - [AuthForm](https://amazon-orders.readthedocs.io/api.html#amazonorders.forms.AuthForm) abstract class, and
264
275
  migrated all auth flow items to subclasses of this class.
265
276
  - [Parsable.simple_parse()](https://amazon-orders.readthedocs.io/api.html#amazonorders.entities.parsable.Parsable.simple_parse),
266
- which can handle most basic fields when parised with CSS selectors.
277
+ which can handle most basic fields when parsed with CSS selectors.
267
278
  - Stability improvements.
268
279
  - Test improvements.
269
280
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: amazon-orders
3
- Version: 3.2.3
3
+ Version: 3.2.4
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
@@ -88,7 +88,7 @@ be used to interact with Amazon.com's consumer-facing website.
88
88
 
89
89
  This works by parsing website data from Amazon.com. A periodic build validates functionality to ensure its
90
90
  stability, but as Amazon provides no official API to use, this package may break at any time. This
91
- package only officially supports the English version of the website.
91
+ package only officially supports the English, `.com` version of Amazon.
92
92
 
93
93
  ## Installation
94
94
 
@@ -12,7 +12,7 @@ be used to interact with Amazon.com's consumer-facing website.
12
12
 
13
13
  This works by parsing website data from Amazon.com. A periodic build validates functionality to ensure its
14
14
  stability, but as Amazon provides no official API to use, this package may break at any time. This
15
- package only officially supports the English version of the website.
15
+ package only officially supports the English, `.com` version of Amazon.
16
16
 
17
17
  ## Installation
18
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: amazon-orders
3
- Version: 3.2.3
3
+ Version: 3.2.4
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
@@ -88,7 +88,7 @@ be used to interact with Amazon.com's consumer-facing website.
88
88
 
89
89
  This works by parsing website data from Amazon.com. A periodic build validates functionality to ensure its
90
90
  stability, but as Amazon provides no official API to use, this package may break at any time. This
91
- package only officially supports the English version of the website.
91
+ package only officially supports the English, `.com` version of Amazon.
92
92
 
93
93
  ## Installation
94
94
 
@@ -1,3 +1,3 @@
1
1
  __copyright__ = "Copyright (c) 2024 Alex Laird"
2
2
  __license__ = "MIT"
3
- __version__ = "3.2.3"
3
+ __version__ = "3.2.4"
@@ -43,7 +43,9 @@ class Order(Parsable):
43
43
  #: The Order number.
44
44
  self.order_number: str = clone.order_number if clone else self.safe_simple_parse(
45
45
  selector=self.config.selectors.FIELD_ORDER_NUMBER_SELECTOR,
46
- required=True)
46
+ required=True,
47
+ prefix_split="#",
48
+ prefix_split_fuzzy=True)
47
49
  #: The Order details link.
48
50
  self.order_details_link: Optional[str] = clone.order_details_link if clone else self.safe_parse(
49
51
  self._parse_order_details_link)
@@ -51,7 +53,10 @@ class Order(Parsable):
51
53
  self.grand_total: float = clone.grand_total if clone else self.safe_parse(self._parse_grand_total)
52
54
  #: The Order placed date.
53
55
  self.order_placed_date: date = clone.order_placed_date if clone else self.safe_simple_parse(
54
- selector=self.config.selectors.FIELD_ORDER_PLACED_DATE_SELECTOR, parse_date=True)
56
+ selector=self.config.selectors.FIELD_ORDER_PLACED_DATE_SELECTOR,
57
+ suffix_split="Order #",
58
+ suffix_split_fuzzy=True,
59
+ parse_date=True)
55
60
  #: The Order Recipients.
56
61
  self.recipient: Recipient = clone.recipient if clone else self.safe_parse(self._parse_recipient)
57
62
 
@@ -69,6 +74,8 @@ class Order(Parsable):
69
74
  self.subtotal: Optional[float] = self._if_full_details(self._parse_currency("subtotal"))
70
75
  #: The Order shipping total. Only populated when ``full_details`` is ``True``.
71
76
  self.shipping_total: Optional[float] = self._if_full_details(self._parse_currency("shipping"))
77
+ #: The Order promotion applied. Only populated when ``full_details`` is ``True``.
78
+ self.promotion_applied: Optional[float] = self._if_full_details(self._parse_currency("promotion"))
72
79
  #: The Order Subscribe & Save discount. Only populated when ``full_details`` is ``True``.
73
80
  self.subscription_discount: Optional[float] = self._if_full_details(self._parse_currency("subscribe"))
74
81
  #: The Order total before tax. Only populated when ``full_details`` is ``True``.
@@ -67,7 +67,10 @@ class Parsable:
67
67
  required: bool = False,
68
68
  prefix_split: Optional[str] = None,
69
69
  wrap_tag: Optional[Type] = None,
70
- parse_date: bool = False) -> Any:
70
+ parse_date: bool = False,
71
+ prefix_split_fuzzy: bool = False,
72
+ suffix_split: Optional[str] = None,
73
+ suffix_split_fuzzy: bool = False) -> Any:
71
74
  """
72
75
  Will attempt to extract the text value of the given CSS selector(s) for a field, and
73
76
  is suitable for most basic functionality on a well-formed page.
@@ -86,6 +89,9 @@ class Parsable:
86
89
  :param wrap_tag: Wrap the selected tag in this class before returning.
87
90
  :param parse_date: ``True`` if the resulting value should be fuzzy parsed in to a date (returning ``None`` if
88
91
  parsing fails).
92
+ :param prefix_split_fuzzy: ``True`` if the value should still be used even if ``prefix_split`` is not found.
93
+ :param suffix_split: Only select the field with the given suffix, returning the left side of the split if so.
94
+ :param suffix_split_fuzzy: ``True`` if the value should still be used even if ``suffix_split`` is not found.
89
95
  :return: The cleaned up return value from the parsed ``selector``.
90
96
  """
91
97
  if isinstance(selector, str):
@@ -109,12 +115,24 @@ class Parsable:
109
115
 
110
116
  if prefix_split:
111
117
  if prefix_split not in tag.text:
112
- continue
118
+ if prefix_split_fuzzy:
119
+ value = tag.text.strip()
120
+ else:
121
+ continue
113
122
  else:
114
123
  value = tag.text.strip().split(prefix_split)[1]
115
124
  else:
116
125
  value = tag.text
117
126
 
127
+ if suffix_split:
128
+ if suffix_split not in value:
129
+ if suffix_split_fuzzy:
130
+ value = value.strip()
131
+ else:
132
+ continue
133
+ else:
134
+ value = value.strip().split(suffix_split)[0]
135
+
118
136
  if wrap_tag:
119
137
  value = wrap_tag(tag, self.config)
120
138
  else:
@@ -142,7 +160,7 @@ class Parsable:
142
160
  """
143
161
  A helper function that uses :func:`simple_parse` as the ``parse_function()`` passed to :func:`safe_parse`.
144
162
 
145
- :param selector: The selector to pass to :func:`simple_parse`.
163
+ :param selector: The CSS selector to pass to :func:`simple_parse`.
146
164
  :param kwargs: The ``kwargs`` will be passed to ``parse_function``.
147
165
  :return: The return value from :func:`simple_parse`.
148
166
  """
@@ -73,9 +73,8 @@ class AmazonOrders:
73
73
  response_parsed = self.amazon_session.last_response_parsed
74
74
 
75
75
  for order_tag in util.select(response_parsed, self.config.selectors.ORDER_HISTORY_ENTITY_SELECTOR):
76
- # If we find a brand logo (for instance, Amazon Fresh), we don't know how to parse this. If we know how
77
- # to do this in the future, we can implement it, but right now we have no reliable way, so skipping
78
- # these orders.
76
+ # First check if this Order is known to be of a type that we do not currently have a way to support
77
+ # parsing, meaning it should be skipped
79
78
  if util.select(order_tag, self.config.selectors.ORDER_HISTORY_BRAND_SELECTOR):
80
79
  continue
81
80
 
@@ -55,7 +55,14 @@ class Selectors:
55
55
  SHIPMENT_ENTITY_SELECTOR = ["[data-component='orderCard'] [data-component='shipments'] .a-box",
56
56
  "div.shipment",
57
57
  "div.delivery-box"]
58
- ORDER_HISTORY_BRAND_SELECTOR = ".brand-info-box .brand-logo img"
58
+ # Selectors defined here, if found in an Order, will cause the Order to be skipped, since it means we currently
59
+ # do not have a way to support fully parsing its details
60
+ ORDER_HISTORY_BRAND_SELECTOR = [
61
+ # Amazon Fresh is not supported
62
+ ".brand-info-box .brand-logo img",
63
+ # Whole Foods Market is not supported
64
+ "a.yohtmlc-order-details-link[href^='/wholefoodsmarket']"
65
+ ]
59
66
 
60
67
  #####################################
61
68
  # CSS selectors for Item fields
@@ -81,12 +88,14 @@ class Selectors:
81
88
  #####################################
82
89
 
83
90
  FIELD_ORDER_DETAILS_LINK_SELECTOR = "a.yohtmlc-order-details-link"
84
- FIELD_ORDER_NUMBER_SELECTOR = [".order-date-invoice-item bdi[dir='ltr']",
91
+ FIELD_ORDER_NUMBER_SELECTOR = ["[data-component='briefOrderInfo'] div.a-column",
92
+ ".order-date-invoice-item bdi[dir='ltr']",
85
93
  "bdi[dir='ltr']",
86
94
  "span[dir='ltr']"]
87
95
  FIELD_ORDER_GRAND_TOTAL_SELECTOR = ["div.yohtmlc-order-total span.value",
88
96
  "div.order-header div.a-column.a-span2"]
89
- FIELD_ORDER_PLACED_DATE_SELECTOR = ["span.order-date-invoice-item",
97
+ FIELD_ORDER_PLACED_DATE_SELECTOR = ["[data-component='briefOrderInfo'] div.a-column",
98
+ "span.order-date-invoice-item",
90
99
  "div.a-span3"]
91
100
  FIELD_ORDER_PAYMENT_METHOD_SELECTOR = "img.pmts-payment-credit-card-instrument-logo"
92
101
  FIELD_ORDER_PAYMENT_METHOD_LAST_4_SELECTOR = "span:has(img.pmts-payment-credit-card-instrument-logo):last-child"
@@ -127,6 +127,30 @@ class TestOrders(UnitTestCase):
127
127
  self.assertEqual(1, resp1.call_count)
128
128
  self.assertEqual(1, resp2.call_count)
129
129
 
130
+ @responses.activate
131
+ def test_get_order_history_skip_wholefoods(self):
132
+ # GIVEN
133
+ self.amazon_session.is_authenticated = True
134
+ year = 2024
135
+ start_index = 0
136
+ resp1 = self.given_order_history_landing_exists()
137
+ with open(os.path.join(self.RESOURCES_DIR, "order-history-wholefoods.html"), "r",
138
+ encoding="utf-8") as f:
139
+ resp2 = responses.add(
140
+ responses.GET,
141
+ self.test_config.constants.ORDER_HISTORY_URL,
142
+ body=f.read(),
143
+ status=200,
144
+ )
145
+
146
+ # WHEN
147
+ orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
148
+
149
+ # THEN
150
+ self.assertEqual(9, len(orders))
151
+ self.assertEqual(1, resp1.call_count)
152
+ self.assertEqual(1, resp2.call_count)
153
+
130
154
  @responses.activate
131
155
  def test_get_order_history_full_details(self):
132
156
  # GIVEN
File without changes
File without changes
File without changes