amazon-orders 2.0.0__tar.gz → 2.0.2__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.0 → amazon_orders-2.0.2}/CHANGELOG.md +39 -19
  2. {amazon_orders-2.0.0/amazon_orders.egg-info → amazon_orders-2.0.2}/PKG-INFO +4 -4
  3. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/README.md +3 -3
  4. {amazon_orders-2.0.0 → amazon_orders-2.0.2/amazon_orders.egg-info}/PKG-INFO +4 -4
  5. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/__init__.py +1 -1
  6. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/cli.py +5 -1
  7. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/conf.py +3 -0
  8. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/order.py +19 -28
  9. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/shipment.py +6 -1
  10. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/orders.py +7 -1
  11. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/selectors.py +2 -2
  12. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/test_conf.py +1 -0
  13. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/test_orders.py +6 -1
  14. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/testcase.py +10 -7
  15. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/LICENSE +0 -0
  16. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/MANIFEST.in +0 -0
  17. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazon_orders.egg-info/SOURCES.txt +0 -0
  18. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazon_orders.egg-info/dependency_links.txt +0 -0
  19. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazon_orders.egg-info/entry_points.txt +0 -0
  20. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazon_orders.egg-info/requires.txt +0 -0
  21. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazon_orders.egg-info/top_level.txt +0 -0
  22. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/banner.txt +0 -0
  23. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/constants.py +0 -0
  24. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/__init__.py +0 -0
  25. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/item.py +0 -0
  26. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/parsable.py +0 -0
  27. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/recipient.py +0 -0
  28. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/entity/seller.py +0 -0
  29. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/exception.py +0 -0
  30. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/forms.py +0 -0
  31. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/session.py +0 -0
  32. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/amazonorders/util.py +0 -0
  33. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/pyproject.toml +0 -0
  34. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/setup.cfg +0 -0
  35. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/test_cli.py +0 -0
  36. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/test_session.py +0 -0
  37. {amazon_orders-2.0.0 → amazon_orders-2.0.2}/tests/test_util.py +0 -0
@@ -4,7 +4,27 @@ 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/2.0.0...HEAD)
7
+ ## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/2.0.2...HEAD)
8
+
9
+ ## [2.0.2](https://github.com/alexdlaird/amazon-orders/compare/2.0.1...2.0.2) - 2024-10-30
10
+
11
+ ### Added
12
+
13
+ - `item_class` to the config file, which allows for overriding the `Item` class.
14
+ - Support for Amazon's new `data-component` tag on order subtotals.
15
+ - Build and stability improvements.
16
+
17
+ ### Fixed
18
+
19
+ - The return value of `Order._parse_recipient()` is now optional, so parsing doesn't break digital goods without a shipping address.
20
+ - Redundant order ID logic to parse from the URI, simplified to consistently fetch from page.
21
+ - Support for order details selector on Amazon's legacy digital orders page.
22
+
23
+ ## [2.0.1](https://github.com/alexdlaird/amazon-orders/compare/2.0.0...2.0.1) - 2024-10-27
24
+
25
+ ### Added
26
+
27
+ Build and stability improvements.
8
28
 
9
29
  ## [2.0.0](https://github.com/alexdlaird/amazon-orders/compare/1.1.4...2.0.0) - 2024-10-26
10
30
 
@@ -18,11 +38,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
18
38
 
19
39
  ### Changed
20
40
 
21
- - Removed global constants in `amazonorders.constants`. Now `amazonorders.constants.Constants` and `amazonorders.selectors.Selectors` classes are used, can be overridden with `constants_class` and `selectors_class` in the config file.
41
+ - Removed global constants in `amazonorders.constants`. Now [`amazonorders.constants.Constants`](https://amazon-orders.readthedocs.io/api.html#amazonorders.constants.Constants) and [`amazonorders.selectors.Selectors`](https://amazon-orders.readthedocs.io/api.html#amazonorders.selectors.Selectors) classes are used, can be overridden with `constants_class` and `selectors_class` in the config file.
22
42
 
23
43
  ### Removed
24
44
 
25
- - `session.AUTH_FORMS`. Pass `auth_forms` when instantiating `AmazonSession` instead.
45
+ - `session.AUTH_FORMS`. Pass [`auth_forms` when instantiating `AmazonSession`](https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession) instead.
26
46
 
27
47
  ## [1.1.4](https://github.com/alexdlaird/amazon-orders/compare/1.1.3...1.1.4) - 2024-06-07
28
48
 
@@ -58,9 +78,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
58
78
 
59
79
  ### Changed
60
80
 
61
- - [AmazonOrder.debug](https://amazon-orders.readthedocs.io/en/1.1.2/api.html#amazonorders.orders.AmazonOrders.debug)
81
+ - [AmazonOrder.debug](https://amazon-orders.readthedocs.io/api.html#amazonorders.orders.AmazonOrders.debug)
62
82
  defaults to the value
63
- of [AmazonSession.debug](https://amazon-orders.readthedocs.io/en/1.1.2/api.html#amazonorders.session.AmazonSession.debug)
83
+ of [AmazonSession.debug](https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession.debug)
64
84
  if an override is not passed.
65
85
 
66
86
  ## [1.1.1](https://github.com/alexdlaird/amazon-orders/compare/1.0.16...1.1.1) - 2024-04-09
@@ -145,12 +165,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
145
165
 
146
166
  ### Added
147
167
 
148
- - [AuthForm](https://amazon-orders.readthedocs.io/en/1.0.9/api.html#amazonorders.forms.AuthForm)'s now
168
+ - [AuthForm](https://amazon-orders.readthedocs.io/api.html#amazonorders.forms.AuthForm)'s now
149
169
  passes `captcha_img_url` to its `prompt()` fallback for Captcha, useful for
150
- overriding [IODefault](https://amazon-orders.readthedocs.io/en/1.0.9/api.html#amazonorders.session.IODefault).
151
- - [MfaDeviceSelectForm](https://amazon-orders.readthedocs.io/en/1.0.9/api.html#amazonorders.forms.MfaDeviceSelectForm)
170
+ overriding [IODefault](https://amazon-orders.readthedocs.io/api.html#amazonorders.session.IODefault).
171
+ - [MfaDeviceSelectForm](https://amazon-orders.readthedocs.io/api.html#amazonorders.forms.MfaDeviceSelectForm)
152
172
  now passes `mfa_device_select_choices` to `prompt()`, useful for
153
- overrides [IODefault](https://amazon-orders.readthedocs.io/en/1.0.9/api.html#amazonorders.session.IODefault).
173
+ overrides [IODefault](https://amazon-orders.readthedocs.io/api.html#amazonorders.session.IODefault).
154
174
  - Documentation improvements.
155
175
 
156
176
  ## [1.0.8](https://github.com/alexdlaird/amazon-orders/compare/1.0.7...1.0.8) - 2024-01-30
@@ -163,9 +183,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
163
183
 
164
184
  ### Added
165
185
 
166
- - [AuthForm](https://amazon-orders.readthedocs.io/en/1.0.7/api.html#amazonorders.forms.AuthForm) abstract class, and
186
+ - [AuthForm](https://amazon-orders.readthedocs.io/api.html#amazonorders.forms.AuthForm) abstract class, and
167
187
  migrated all auth flow items to subclasses of this class.
168
- - [Parsable.simple_parse()](https://amazon-orders.readthedocs.io/en/1.0.7/api.html#amazonorders.entities.parsable.Parsable.simple_parse),
188
+ - [Parsable.simple_parse()](https://amazon-orders.readthedocs.io/api.html#amazonorders.entities.parsable.Parsable.simple_parse),
169
189
  which can handle most basic fields when parised with CSS selectors.
170
190
  - Stability improvements.
171
191
  - Test improvements.
@@ -192,17 +212,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
192
212
 
193
213
  ### Added
194
214
 
195
- - [Item.image_link](https://amazon-orders.readthedocs.io/en/1.0.5/api.html#amazonorders.entity.item.Item.image_link).
196
- - [Item.quantity](https://amazon-orders.readthedocs.io/en/1.0.5/api.html#amazonorders.entity.item.Item.quantity).
215
+ - [Item.image_link](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.item.Item.image_link).
216
+ - [Item.quantity](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.item.Item.quantity).
197
217
  - `version` command to CLI.
198
218
  - Test improvements.
199
219
 
200
220
  ### Changed
201
221
 
202
222
  - Migrated to using CSS selectors
203
- in [`AmazonSession`](https://amazon-orders.readthedocs.io/en/1.0.5/api.html#amazonorders.session.AmazonSession)
223
+ in [`AmazonSession`](https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession)
204
224
  - Migrated to using CSS selectors
205
- in [`AmazonOrders`](https://amazon-orders.readthedocs.io/en/1.0.5/api.html#amazonorders.orders.AmazonOrders)
225
+ in [`AmazonOrders`](https://amazon-orders.readthedocs.io/api.html#amazonorders.orders.AmazonOrders)
206
226
 
207
227
  ## [1.0.4](https://github.com/alexdlaird/amazon-orders/compare/1.0.3...1.0.4) - 2024-01-24
208
228
 
@@ -211,13 +231,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
211
231
  - A new OTP auth flow from Amazon that can occur after Captcha.
212
232
  - Parameters `--max-auth-attempts` and `--output-dir` to CLI.
213
233
  - `DEFAULT_OUTPUT_DIR`, which defaults to `os.getcwd()`, but allows users to change where output files are written.
214
- - [`Troubleshooting`](https://amazon-orders.readthedocs.io/en/1.0.4/troubleshooting.html) section to the docs.
234
+ - [`Troubleshooting`](https://amazon-orders.readthedocs.io/troubleshooting.html) section to the docs.
215
235
  - Test improvements, including the ability to run dynamic tests using private order data from JSON files.
216
236
 
217
237
  ### Changed
218
238
 
219
239
  - Improved string representations of entities,
220
- including [`Order`](https://amazon-orders.readthedocs.io/en/1.0.4/api.html#amazonorders.entity.order.Order), moved
240
+ including [`Order`](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.order.Order), moved
221
241
  string representation of all fields back to `cli.py` out of the `__str__` method.
222
242
  - Moved `DEFAULT_COOKIE_JAR_PATH` to `conf.py`.
223
243
 
@@ -246,7 +266,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
246
266
 
247
267
  - Auth flow now also checks session cookies in addition to parsing the page for signs of login.
248
268
  - All fields to string representation
249
- of [`Order`](https://amazon-orders.readthedocs.io/en/1.0.1/api.html#amazonorders.entity.order.Order), so they are not
269
+ of [`Order`](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.order.Order), so they are not
250
270
  output on the CLI.
251
271
  - `logout` command to CLI.
252
272
  - Documentation improvements.
@@ -255,7 +275,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
255
275
  ### Fixed
256
276
 
257
277
  - Improvements to CLI, including error message cleanup on auth exceptions.
258
- - [`Order.order_details_link`](https://amazon-orders.readthedocs.io/en/1.0.1/api.html#amazonorders.entity.order.Order.order_details_link)
278
+ - [`Order.order_details_link`](https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.order.Order.order_details_link)
259
279
  is now properly populated even on the details page.
260
280
  - `.gitattributes` to HTML files are now ignore by Linguist.
261
281
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amazon-orders
3
- Version: 2.0.0
3
+ Version: 2.0.2
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
@@ -71,7 +71,7 @@ Requires-Dist: types-beautifulsoup4; extra == "docs"
71
71
  Requires-Dist: types-Pillow; extra == "docs"
72
72
  Requires-Dist: types-PyYAML; extra == "docs"
73
73
 
74
- <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
74
+ <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
75
75
 
76
76
  [![Version](https://img.shields.io/pypi/v/amazon-orders)](https://pypi.org/project/amazon-orders)
77
77
  [![Python Versions](https://img.shields.io/pypi/pyversions/amazon-orders.svg)](https://pypi.org/project/amazon-orders)
@@ -83,9 +83,9 @@ Requires-Dist: types-PyYAML; extra == "docs"
83
83
  `amazon-orders` is an unofficial library that provides a command line interface alongside a programmatic API that can
84
84
  be used to interact with Amazon.com's consumer-facing website.
85
85
 
86
- This works by parsing website data from Amazon.com. A period build validates functionality to ensure its
86
+ This works by parsing website data from Amazon.com. A periodic build validates functionality to ensure its
87
87
  stability, but as Amazon provides no official API to use, this package may break at any time. This
88
- package only supports the English version of the website.
88
+ package only officially supports the English version of the website.
89
89
 
90
90
  ## Installation
91
91
 
@@ -1,4 +1,4 @@
1
- <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
1
+ <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
2
2
 
3
3
  [![Version](https://img.shields.io/pypi/v/amazon-orders)](https://pypi.org/project/amazon-orders)
4
4
  [![Python Versions](https://img.shields.io/pypi/pyversions/amazon-orders.svg)](https://pypi.org/project/amazon-orders)
@@ -10,9 +10,9 @@
10
10
  `amazon-orders` is an unofficial library that provides a command line interface alongside a programmatic API that can
11
11
  be used to interact with Amazon.com's consumer-facing website.
12
12
 
13
- This works by parsing website data from Amazon.com. A period build validates functionality to ensure its
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 supports the English version of the website.
15
+ package only officially supports the English version of the website.
16
16
 
17
17
  ## Installation
18
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amazon-orders
3
- Version: 2.0.0
3
+ Version: 2.0.2
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
@@ -71,7 +71,7 @@ Requires-Dist: types-beautifulsoup4; extra == "docs"
71
71
  Requires-Dist: types-Pillow; extra == "docs"
72
72
  Requires-Dist: types-PyYAML; extra == "docs"
73
73
 
74
- <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/en/latest/_images/logo.png" /></p>
74
+ <p align="center"><img alt="amazon-orders - a python library" src="https://amazon-orders.readthedocs.io/_images/logo.png" /></p>
75
75
 
76
76
  [![Version](https://img.shields.io/pypi/v/amazon-orders)](https://pypi.org/project/amazon-orders)
77
77
  [![Python Versions](https://img.shields.io/pypi/pyversions/amazon-orders.svg)](https://pypi.org/project/amazon-orders)
@@ -83,9 +83,9 @@ Requires-Dist: types-PyYAML; extra == "docs"
83
83
  `amazon-orders` is an unofficial library that provides a command line interface alongside a programmatic API that can
84
84
  be used to interact with Amazon.com's consumer-facing website.
85
85
 
86
- This works by parsing website data from Amazon.com. A period build validates functionality to ensure its
86
+ This works by parsing website data from Amazon.com. A periodic build validates functionality to ensure its
87
87
  stability, but as Amazon provides no official API to use, this package may break at any time. This
88
- package only supports the English version of the website.
88
+ package only officially supports the English version of the website.
89
89
 
90
90
  ## Installation
91
91
 
@@ -1,3 +1,3 @@
1
1
  __copyright__ = "Copyright (c) 2024 Alex Laird"
2
2
  __license__ = "MIT"
3
- __version__ = "2.0.0"
3
+ __version__ = "2.0.2"
@@ -278,7 +278,11 @@ Order #{}
278
278
  order_str += f"\n Order Details Link: {order.order_details_link}"
279
279
  order_str += f"\n Grand Total: ${order.grand_total:,.2f}"
280
280
  order_str += f"\n Order Placed Date: {order.order_placed_date}"
281
- order_str += f"\n {order.recipient}"
281
+ if order.recipient:
282
+ order_str += f"\n {order.recipient}"
283
+ else:
284
+ order_str += "\n Recipient: None"
285
+
282
286
  if order.payment_method:
283
287
  order_str += f"\n Payment Method: {order.payment_method}"
284
288
  if order.payment_method_last_4:
@@ -35,6 +35,7 @@ class AmazonOrdersConfig:
35
35
  "selectors_class": "amazonorders.selectors.Selectors",
36
36
  "order_class": "amazonorders.entity.order.Order",
37
37
  "shipment_class": "amazonorders.entity.shipment.Shipment",
38
+ "item_class": "amazonorders.entity.item.Item",
38
39
  }
39
40
 
40
41
  if os.path.exists(self.config_path):
@@ -62,11 +63,13 @@ class AmazonOrdersConfig:
62
63
  selectors_class_split = self.selectors_class.split(".")
63
64
  order_class_split = self.order_class.split(".")
64
65
  shipment_class_split = self.shipment_class.split(".")
66
+ item_class_split = self.item_class.split(".")
65
67
 
66
68
  self.constants = util.load_class(constants_class_split[:-1], constants_class_split[-1])()
67
69
  self.selectors = util.load_class(selectors_class_split[:-1], selectors_class_split[-1])()
68
70
  self.order_cls = util.load_class(order_class_split[:-1], order_class_split[-1])
69
71
  self.shipment_cls = util.load_class(shipment_class_split[:-1], shipment_class_split[-1])
72
+ self.item_cls = util.load_class(item_class_split[:-1], item_class_split[-1])
70
73
 
71
74
  def __getattr__(self,
72
75
  key: str) -> Any:
@@ -4,8 +4,7 @@ __license__ = "MIT"
4
4
  import json
5
5
  import logging
6
6
  from datetime import date, datetime
7
- from typing import List, Optional, TypeVar, Union, Any
8
- from urllib.parse import parse_qs, urlparse
7
+ from typing import Any, List, Optional, TypeVar, Union
9
8
 
10
9
  from bs4 import BeautifulSoup, Tag
11
10
 
@@ -15,7 +14,6 @@ from amazonorders.entity.item import Item
15
14
  from amazonorders.entity.parsable import Parsable
16
15
  from amazonorders.entity.recipient import Recipient
17
16
  from amazonorders.entity.shipment import Shipment
18
- from amazonorders.exception import AmazonOrdersError
19
17
 
20
18
  logger = logging.getLogger(__name__)
21
19
 
@@ -42,7 +40,9 @@ class Order(Parsable):
42
40
  #: The Order Items.
43
41
  self.items: List[Item] = clone.items if clone and not full_details else self._parse_items()
44
42
  #: The Order number.
45
- self.order_number: str = clone.order_number if clone else self.safe_parse(self._parse_order_number)
43
+ self.order_number: str = clone.order_number if clone else self.safe_simple_parse(
44
+ selector=self.config.selectors.FIELD_ORDER_NUMBER_SELECTOR,
45
+ required=True)
46
46
  #: The Order details link.
47
47
  self.order_details_link: Optional[str] = clone.order_details_link if clone else self.safe_parse(
48
48
  self._parse_order_details_link)
@@ -84,6 +84,9 @@ class Order(Parsable):
84
84
  return f"Order #{self.order_number}: {self.items}"
85
85
 
86
86
  def _parse_shipments(self) -> List[Shipment]:
87
+ if not self.parsed:
88
+ return []
89
+
87
90
  shipments: List[Shipment] = [self.config.shipment_cls(x, self.config)
88
91
  for x in util.select(self.parsed,
89
92
  self.config.selectors.SHIPMENT_ENTITY_SELECTOR)]
@@ -91,8 +94,12 @@ class Order(Parsable):
91
94
  return shipments
92
95
 
93
96
  def _parse_items(self) -> List[Item]:
94
- items = [Item(x, self.config)
95
- for x in util.select(self.parsed, self.config.selectors.ITEM_ENTITY_SELECTOR)]
97
+ if not self.parsed:
98
+ return []
99
+
100
+ items: List[Item] = [self.config.item_cls(x, self.config)
101
+ for x in util.select(self.parsed,
102
+ self.config.selectors.ITEM_ENTITY_SELECTOR)]
96
103
  items.sort()
97
104
  return items
98
105
 
@@ -104,21 +111,6 @@ class Order(Parsable):
104
111
 
105
112
  return value
106
113
 
107
- def _parse_order_number(self) -> str:
108
- try:
109
- order_details_link = self._parse_order_details_link()
110
- except Exception:
111
- # We're not using safe_parse here because it's fine if this fails, no need for noise
112
- order_details_link = None
113
-
114
- if order_details_link:
115
- parsed_url = urlparse(order_details_link)
116
- value = parse_qs(parsed_url.query)["orderID"][0]
117
- else:
118
- value = self.simple_parse(self.config.selectors.FIELD_ORDER_NUMBER_SELECTOR, required=True)
119
-
120
- return value
121
-
122
114
  def _parse_grand_total(self) -> float:
123
115
  value = self.simple_parse(self.config.selectors.FIELD_ORDER_GRAND_TOTAL_SELECTOR)
124
116
 
@@ -150,7 +142,7 @@ class Order(Parsable):
150
142
 
151
143
  return value
152
144
 
153
- def _parse_recipient(self) -> Recipient:
145
+ def _parse_recipient(self) -> Optional[Recipient]:
154
146
  value = util.select_one(self.parsed, self.config.selectors.FIELD_ORDER_ADDRESS_SELECTOR)
155
147
 
156
148
  if not value:
@@ -172,13 +164,12 @@ class Order(Parsable):
172
164
  parsed_parent,
173
165
  self.config.selectors.FIELD_ORDER_ADDRESS_FALLBACK_2_SELECTOR
174
166
  )
175
- if not parent_tag:
176
- raise AmazonOrdersError(
177
- "FIELD_ORDER_ADDRESS_FALLBACK_2_SELECTOR resulted in None, but it's required. "
178
- "Check if Amazon changed the expected HTML."
179
- ) # pragma: no cover
180
167
 
181
- value = BeautifulSoup(str(parent_tag.contents[0]).strip(), "html.parser")
168
+ if parent_tag:
169
+ value = BeautifulSoup(str(parent_tag.contents[0]).strip(), "html.parser")
170
+
171
+ if not value:
172
+ return None
182
173
 
183
174
  return Recipient(value, self.config)
184
175
 
@@ -50,6 +50,11 @@ class Shipment(Parsable):
50
50
  return str(self.items) < str(other.items)
51
51
 
52
52
  def _parse_items(self) -> List[Item]:
53
- items = [Item(x, self.config) for x in util.select(self.parsed, self.config.selectors.ITEM_ENTITY_SELECTOR)]
53
+ if not self.parsed:
54
+ return []
55
+
56
+ items: List[Item] = [self.config.item_cls(x, self.config)
57
+ for x in util.select(self.parsed,
58
+ self.config.selectors.ITEM_ENTITY_SELECTOR)]
54
59
  items.sort()
55
60
  return items
@@ -73,9 +73,15 @@ 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
- order = self.config.order_cls(order_tag, self.config)
76
+ order: Order = self.config.order_cls(order_tag, self.config)
77
77
 
78
78
  if full_details:
79
+ if not order.order_details_link:
80
+ logger.warning(f"order_details_link for Order {order.order_number} did not populate, "
81
+ f"cannot read full details.")
82
+
83
+ continue
84
+
79
85
  self.amazon_session.get(order.order_details_link)
80
86
  order_details_tag = util.select_one(self.amazon_session.last_response_parsed,
81
87
  self.config.selectors.ORDER_DETAILS_ENTITY_SELECTOR)
@@ -36,7 +36,7 @@ class Selectors:
36
36
  ##########################################################################
37
37
 
38
38
  ORDER_HISTORY_ENTITY_SELECTOR = ["div.order", "div.order-card"]
39
- ORDER_DETAILS_ENTITY_SELECTOR = ["div#orderDetails", "[data-component='orderCard']"]
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
41
  SHIPMENT_ENTITY_SELECTOR = ["div.shipment", "div.delivery-box", "[data-component='shipments']"]
42
42
 
@@ -61,7 +61,7 @@ class Selectors:
61
61
  "div.a-span3"]
62
62
  FIELD_ORDER_PAYMENT_METHOD_SELECTOR = "img.pmts-payment-credit-card-instrument-logo"
63
63
  FIELD_ORDER_PAYMENT_METHOD_LAST_4_SELECTOR = "img.pmts-payment-credit-card-instrument-logo"
64
- FIELD_ORDER_SUBTOTALS_TAG_ITERATOR_SELECTOR = "div#od-subtotals div.a-row"
64
+ FIELD_ORDER_SUBTOTALS_TAG_ITERATOR_SELECTOR = ["div#od-subtotals div.a-row", "[data-component='orderSubtotals']"]
65
65
  FIELD_ORDER_SUBTOTALS_INNER_TAG_SELECTOR = "div.a-span-last"
66
66
  FIELD_ORDER_ADDRESS_SELECTOR = "div.displayAddressDiv"
67
67
  FIELD_ORDER_ADDRESS_FALLBACK_1_SELECTOR = "div.recipient span.a-declarative"
@@ -50,6 +50,7 @@ class TestConf(TestCase):
50
50
  with open(config.config_path, "r") as f:
51
51
  self.assertEqual("""constants_class: amazonorders.constants.Constants
52
52
  cookie_jar_path: {}
53
+ item_class: amazonorders.entity.item.Item
53
54
  max_auth_attempts: 10
54
55
  order_class: amazonorders.entity.order.Order
55
56
  output_dir: {}
@@ -204,7 +204,8 @@ class TestOrders(UnitTestCase):
204
204
  @responses.activate
205
205
  def test_temp_order_history_file(self):
206
206
  """
207
- This test
207
+ This test can be used to drop in an order history page at tests/output/temp-order-history.html to easily
208
+ run a test against it for debugging purposes.
208
209
  """
209
210
  # GIVEN
210
211
  self.amazon_session.is_authenticated = True
@@ -239,6 +240,10 @@ class TestOrders(UnitTestCase):
239
240
  "place it at tests/output/temp-order-details.html")
240
241
  @responses.activate
241
242
  def test_temp_order_details_file(self):
243
+ """
244
+ This test can be used to drop in an order details page at tests/output/temp-order-details.html to easily
245
+ run a test against it for debugging purposes.
246
+ """
242
247
  # GIVEN
243
248
  self.amazon_session.is_authenticated = True
244
249
  order_id = "temp-1234"
@@ -358,10 +358,11 @@ class TestCase(unittest.TestCase):
358
358
  self.assertIsNotNone(order.grand_total)
359
359
  self.assertIsNotNone(order.order_details_link)
360
360
  self.assertIsNotNone(order.order_placed_date)
361
- self.assertIsNotNone(order.recipient.name)
362
- self.assertIsNotNone(order.recipient.address)
363
- self.assertGreaterEqual(len(order.shipments), 1)
364
- self.assertEqual(str(order.items), str(order.shipments[0].items))
361
+ if order.recipient:
362
+ self.assertIsNotNone(order.recipient.name)
363
+ self.assertIsNotNone(order.recipient.address)
364
+ self.assertGreaterEqual(len(order.shipments), 1)
365
+ self.assertEqual(str(order.items), str(order.shipments[0].items))
365
366
  self.assertGreaterEqual(len(order.items), 1)
366
367
  self.assertIsNotNone(order.items[0].title)
367
368
  self.assertIsNotNone(order.items[0].link)
@@ -372,10 +373,12 @@ class TestCase(unittest.TestCase):
372
373
  self.assertIsNotNone(order.payment_method)
373
374
  self.assertEqual(4, len(order.payment_method_last_4))
374
375
  self.assertIsNotNone(order.subtotal)
375
- self.assertIsNotNone(order.shipping_total)
376
+ if order.recipient:
377
+ self.assertIsNotNone(order.shipping_total)
376
378
  self.assertIsNotNone(order.total_before_tax)
377
379
  self.assertIsNotNone(order.estimated_tax)
378
380
  # As of April 2024, this is no longer shown in Order History
379
381
  # self.assertIsNotNone(order.items[0].condition)
380
- self.assertIsNotNone(order.items[0].price)
381
- self.assertIsNotNone(order.items[0].seller.name)
382
+ if order.recipient:
383
+ self.assertIsNotNone(order.items[0].price)
384
+ self.assertIsNotNone(order.items[0].seller.name)
File without changes
File without changes
File without changes