amazon-orders 5.0.0__tar.gz → 5.2.0__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-5.0.0 → amazon_orders-5.2.0}/CHANGELOG.md +26 -1
- {amazon_orders-5.0.0/amazon_orders.egg-info → amazon_orders-5.2.0}/PKG-INFO +2 -2
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/README.md +1 -1
- {amazon_orders-5.0.0 → amazon_orders-5.2.0/amazon_orders.egg-info}/PKG-INFO +2 -2
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/__init__.py +1 -1
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/cli.py +36 -18
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/constants.py +1 -1
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/family_member.py +1 -1
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/orders.py +58 -19
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/selectors.py +3 -1
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/session.py +29 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/LICENSE +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/MANIFEST.in +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/banner.txt +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/conf.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/__init__.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/browser/__init__.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/browser/playwright.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/waf/__init__.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/waf/anticaptcha.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/waf/base.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/waf/capsolver.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/contrib/waf/twocaptcha.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/gift_card_activity.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/invoice.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/item.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/order.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/order_search_result.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/parsable.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/prime_payment.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/tracking.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/entity/transaction.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/exception.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/forms.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/gift_cards.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/output.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/paging.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/prime.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/py.typed +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/region_data.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/transactions.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/amazonorders/util.py +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/pyproject.toml +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/setup.cfg +0 -0
- {amazon_orders-5.0.0 → amazon_orders-5.2.0}/tests/testcase.py +0 -0
|
@@ -4,7 +4,32 @@ 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/5.
|
|
7
|
+
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/5.2.0...HEAD)
|
|
8
|
+
|
|
9
|
+
## [5.2.0](https://github.com/alexdlaird/amazon-orders/compare/5.1.0...5.2.0) - 2026-10-25
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `Orders`, which `get_orders_page()` now returns, with `household_members`: the Amazon Household members whose Orders the account can view, read from the same page, to pass as `family_member_id`. The `orders` command lists them after the Orders.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- An account in an Amazon Household gets the whole household's Orders by default, as Amazon's Order history now does. Pass `family_member_id="self"` (or `--family-member-id self`) for only the account's own.
|
|
18
|
+
|
|
19
|
+
### Deprecated
|
|
20
|
+
|
|
21
|
+
- `AmazonOrders.get_family_members()`, use `get_orders_page()`'s `household_members`.
|
|
22
|
+
- The `family-members` command, use `orders`, which lists the Amazon Household members after the Orders.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `get_family_members()` and `family_member_id` use Amazon's new `householdMembers` Order history filter, which replaced `familyMemberId`.
|
|
27
|
+
|
|
28
|
+
## [5.1.0](https://github.com/alexdlaird/amazon-orders/compare/5.0.0...5.1.0) - 2026-10-24
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `AmazonSession.check_session(verify=True)` and `check-session --verify`, to ask Amazon whether it still accepts the persisted session.
|
|
8
33
|
|
|
9
34
|
## [5.0.0](https://github.com/alexdlaird/amazon-orders/compare/4.12.0...5.0.0) - 2026-10-23
|
|
10
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amazon-orders
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0
|
|
4
4
|
Summary: A Python library (and CLI) for Amazon order history, line items, and transactions
|
|
5
5
|
Author-email: Alex Laird <contact@alexlaird.com>
|
|
6
6
|
Maintainer-email: Alex Laird <contact@alexlaird.com>
|
|
@@ -98,7 +98,7 @@ pip install amazon-orders --upgrade
|
|
|
98
98
|
|
|
99
99
|
That's it! `amazon-orders` is now available as a package to your Python projects and from the command line.
|
|
100
100
|
|
|
101
|
-
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.
|
|
101
|
+
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.2.*`, not `==5.2.0`) to
|
|
102
102
|
ensure you always get the latest stable release.
|
|
103
103
|
|
|
104
104
|
## Basic Usage
|
|
@@ -24,7 +24,7 @@ pip install amazon-orders --upgrade
|
|
|
24
24
|
|
|
25
25
|
That's it! `amazon-orders` is now available as a package to your Python projects and from the command line.
|
|
26
26
|
|
|
27
|
-
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.
|
|
27
|
+
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.2.*`, not `==5.2.0`) to
|
|
28
28
|
ensure you always get the latest stable release.
|
|
29
29
|
|
|
30
30
|
## Basic Usage
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amazon-orders
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0
|
|
4
4
|
Summary: A Python library (and CLI) for Amazon order history, line items, and transactions
|
|
5
5
|
Author-email: Alex Laird <contact@alexlaird.com>
|
|
6
6
|
Maintainer-email: Alex Laird <contact@alexlaird.com>
|
|
@@ -98,7 +98,7 @@ pip install amazon-orders --upgrade
|
|
|
98
98
|
|
|
99
99
|
That's it! `amazon-orders` is now available as a package to your Python projects and from the command line.
|
|
100
100
|
|
|
101
|
-
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.
|
|
101
|
+
If pinning, be sure to use a wildcard for the [minor version](https://semver.org/) (e.g. `==5.2.*`, not `==5.2.0`) to
|
|
102
102
|
ensure you always get the latest stable release.
|
|
103
103
|
|
|
104
104
|
## Basic Usage
|
|
@@ -308,8 +308,8 @@ def amazon_orders_cli(ctx: Context,
|
|
|
308
308
|
help="The order type filter to use, e.g. digital-orders for Kindle and other digital purchases, "
|
|
309
309
|
"which the default history doesn't include.")
|
|
310
310
|
@click.option("--family-member-id", "family_member_id", default=None,
|
|
311
|
-
help="Get the Orders of this Amazon Household member, such as a linked Teen account, instead
|
|
312
|
-
"
|
|
311
|
+
help="Get the Orders of this Amazon Household member, such as a linked Teen account, instead, or use "
|
|
312
|
+
"self for only your own Orders. The members' IDs are listed after the Orders.")
|
|
313
313
|
@click.option("-o", "--output", type=click.Choice(OutputFormatter.OUTPUT_FORMATS), default="text",
|
|
314
314
|
help="The output format. Defaults to text.")
|
|
315
315
|
def orders(ctx: Context,
|
|
@@ -367,21 +367,26 @@ Order History for {filter_description}{optional_start_index}{optional_full_detai
|
|
|
367
367
|
|
|
368
368
|
start_time = time.time()
|
|
369
369
|
with ProgressSpinner("Orders", ctx.obj["show_progress"]) as progress:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
370
|
+
orders_page = amazon_orders.get_orders_page(year=year,
|
|
371
|
+
start_index=start_index,
|
|
372
|
+
full_details=full_details,
|
|
373
|
+
time_filter=time_filter,
|
|
374
|
+
order_filter=order_filter,
|
|
375
|
+
limit=1 if single_page else None,
|
|
376
|
+
family_member_id=family_member_id,
|
|
377
|
+
progress_with_total=progress)
|
|
378
378
|
end_time = time.time()
|
|
379
|
+
order_list = orders_page.items
|
|
379
380
|
|
|
380
|
-
|
|
381
|
+
formatter = config.output_cls(config)
|
|
382
|
+
click.echo(formatter.format(order_list, output))
|
|
381
383
|
|
|
382
384
|
click.echo(
|
|
383
385
|
"... {total} Orders parsed in {time} seconds.\n".format(total=len(order_list),
|
|
384
386
|
time=int(end_time - start_time)), err=True)
|
|
387
|
+
if orders_page.household_members:
|
|
388
|
+
click.echo("Amazon Household members, for --family-member-id:\n\n{members}".format(
|
|
389
|
+
members=formatter.format(orders_page.household_members, "text")), err=True)
|
|
385
390
|
except AmazonOrdersAuthRedirectError:
|
|
386
391
|
IOClick.echo_session_expired()
|
|
387
392
|
except AmazonOrdersError as e:
|
|
@@ -632,13 +637,13 @@ Transaction History for {days} days
|
|
|
632
637
|
ctx.fail(str(e))
|
|
633
638
|
|
|
634
639
|
|
|
635
|
-
@amazon_orders_cli.command("family-members")
|
|
640
|
+
@amazon_orders_cli.command("family-members", hidden=True, deprecated=True)
|
|
636
641
|
@click.pass_context
|
|
637
642
|
@click.option("-o", "--output", type=click.Choice(OutputFormatter.OUTPUT_FORMATS), default="text",
|
|
638
643
|
help="The output format. Defaults to text.")
|
|
639
644
|
def family_members(ctx: Context, **kwargs: Any) -> None:
|
|
640
645
|
"""
|
|
641
|
-
|
|
646
|
+
Deprecated, use orders instead, which lists the Amazon Household members after the Orders.
|
|
642
647
|
"""
|
|
643
648
|
amazon_session = ctx.obj["amazon_session"]
|
|
644
649
|
|
|
@@ -721,15 +726,28 @@ Gift Cards, with {days} days of activity
|
|
|
721
726
|
|
|
722
727
|
@amazon_orders_cli.command(short_help="Check if a persisted session exists.")
|
|
723
728
|
@click.pass_context
|
|
724
|
-
|
|
729
|
+
@click.option("--verify", is_flag=True, default=False,
|
|
730
|
+
help="Also ask Amazon whether it still accepts the persisted session.")
|
|
731
|
+
def check_session(ctx: Context, **kwargs: Any) -> None:
|
|
725
732
|
"""
|
|
726
733
|
Check if a persisted session exists, meaning commands can be called without needing to provide credentials.
|
|
727
734
|
"""
|
|
728
735
|
amazon_session = ctx.obj["amazon_session"]
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
736
|
+
|
|
737
|
+
try:
|
|
738
|
+
if amazon_session.check_session(verify=kwargs["verify"]):
|
|
739
|
+
if kwargs["verify"]:
|
|
740
|
+
click.echo("Info: A persisted session exists, and Amazon accepts it.\n")
|
|
741
|
+
else:
|
|
742
|
+
click.echo("Info: A persisted session exists.\n")
|
|
743
|
+
elif amazon_session.auth_cookies_stored():
|
|
744
|
+
click.echo("Info: A persisted session exists, but Amazon no longer accepts it.\n")
|
|
745
|
+
IOClick.echo_session_expired()
|
|
746
|
+
else:
|
|
747
|
+
click.echo("Info: No persisted session exists.\n")
|
|
748
|
+
except AmazonOrdersError as e:
|
|
749
|
+
logger.debug("An error occurred.", exc_info=True)
|
|
750
|
+
ctx.fail(str(e))
|
|
733
751
|
|
|
734
752
|
|
|
735
753
|
@amazon_orders_cli.command()
|
|
@@ -67,7 +67,7 @@ class Constants:
|
|
|
67
67
|
ORDER_INVOICE_URL = f"{BASE_URL}/gp/css/summary/print.html"
|
|
68
68
|
HISTORY_FILTER_QUERY_PARAM = "timeFilter"
|
|
69
69
|
ORDER_FILTER_QUERY_PARAM = "orderFilter"
|
|
70
|
-
FAMILY_MEMBER_QUERY_PARAM = "
|
|
70
|
+
FAMILY_MEMBER_QUERY_PARAM = "householdMembers"
|
|
71
71
|
HISTORY_PAGE_QUERY_PARAM = "page"
|
|
72
72
|
HISTORY_START_INDEX_QUERY_PARAM = "startIndex"
|
|
73
73
|
# Amazon's no-JavaScript fallback, which renders encrypted Order cards readable
|
|
@@ -24,7 +24,7 @@ class FamilyMember(Parsable):
|
|
|
24
24
|
config: AmazonOrdersConfig) -> None:
|
|
25
25
|
super().__init__(parsed, config)
|
|
26
26
|
|
|
27
|
-
#: The FamilyMember ID, Amazon's ``
|
|
27
|
+
#: The FamilyMember ID, the value Amazon's household member filter (``householdMembers``) takes.
|
|
28
28
|
self.id: Optional[str] = self.safe_parse(self._parse_id)
|
|
29
29
|
#: The FamilyMember name.
|
|
30
30
|
self.name: Optional[str] = self.safe_parse(self._parse_name)
|
|
@@ -5,6 +5,7 @@ import asyncio
|
|
|
5
5
|
import concurrent.futures
|
|
6
6
|
import datetime
|
|
7
7
|
import logging
|
|
8
|
+
import warnings
|
|
8
9
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
9
10
|
from urllib.parse import parse_qs, quote, urlencode, urlsplit
|
|
10
11
|
|
|
@@ -24,6 +25,38 @@ from amazonorders.session import AmazonSession
|
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
class Orders(PagedResults[Order]):
|
|
29
|
+
"""
|
|
30
|
+
A batch of the Amazon Order history, with the Amazon Household members whose Orders the account can view and the
|
|
31
|
+
cursor to fetch the next batch, from :func:`~amazonorders.orders.AmazonOrders.get_orders_page`.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self,
|
|
35
|
+
items: List[Order],
|
|
36
|
+
cursor: Optional[Dict[str, Any]],
|
|
37
|
+
household_members: List[FamilyMember]) -> None:
|
|
38
|
+
super().__init__(items, cursor)
|
|
39
|
+
|
|
40
|
+
#: The Amazon Household members whose Orders the account can view, such as linked Teen accounts, from the
|
|
41
|
+
#: Order history's member filter. Pass a member's :attr:`~amazonorders.entity.family_member.FamilyMember.id`
|
|
42
|
+
#: as ``family_member_id`` to get their Orders. Empty if the account has none.
|
|
43
|
+
self.household_members: List[FamilyMember] = household_members
|
|
44
|
+
|
|
45
|
+
def to_dict(self,
|
|
46
|
+
exclude_none: bool = False) -> Dict[str, Any]:
|
|
47
|
+
"""
|
|
48
|
+
Serialize the batch to a ``dict`` of primitives, suitable for JSON, as
|
|
49
|
+
:func:`~amazonorders.paging.PagedResults.to_dict` does, with the ``household_members``.
|
|
50
|
+
|
|
51
|
+
:param exclude_none: Passed to each Order's and FamilyMember's ``to_dict``.
|
|
52
|
+
:return: The ``household_members``, ``items``, ``cursor``, and ``has_more``.
|
|
53
|
+
"""
|
|
54
|
+
return {
|
|
55
|
+
"household_members": [member.to_dict(exclude_none=exclude_none) for member in self.household_members],
|
|
56
|
+
**super().to_dict(exclude_none=exclude_none),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
27
60
|
class AmazonOrders:
|
|
28
61
|
"""
|
|
29
62
|
Using an authenticated :class:`~amazonorders.session.AmazonSession`, can be used to query Amazon
|
|
@@ -105,7 +138,8 @@ class AmazonOrders:
|
|
|
105
138
|
config: AmazonOrdersConfig) -> List[FamilyMember]:
|
|
106
139
|
"""
|
|
107
140
|
Parse the Amazon Household members from an already-fetched Order history page, without a session driving
|
|
108
|
-
the fetch. Useful for parsing HTML
|
|
141
|
+
the fetch, as :attr:`~amazonorders.orders.Orders.household_members` holds them. Useful for parsing HTML
|
|
142
|
+
obtained elsewhere and for network-free testing.
|
|
109
143
|
|
|
110
144
|
:param html: The Order history page HTML to parse.
|
|
111
145
|
:param config: The config providing the selectors used for parsing.
|
|
@@ -253,13 +287,13 @@ class AmazonOrders:
|
|
|
253
287
|
|
|
254
288
|
def get_family_members(self) -> List[FamilyMember]:
|
|
255
289
|
"""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
:attr:`~amazonorders.entity.family_member.FamilyMember.id` as ``family_member_id`` to
|
|
259
|
-
:func:`get_orders` to get their Orders.
|
|
290
|
+
Deprecated, use :attr:`~amazonorders.orders.Orders.household_members` from :func:`get_orders_page`
|
|
291
|
+
instead. To be removed in 6.0.
|
|
260
292
|
|
|
261
293
|
:return: The FamilyMembers, or an empty list if the account has none.
|
|
262
294
|
"""
|
|
295
|
+
warnings.warn("get_family_members() is deprecated and will be removed in 6.0. Use get_orders_page(), whose "
|
|
296
|
+
"household_members lists them, instead.", DeprecationWarning, stacklevel=2)
|
|
263
297
|
if not self.amazon_session.is_authenticated:
|
|
264
298
|
raise AmazonOrdersError("Call AmazonSession.login() to authenticate first.")
|
|
265
299
|
|
|
@@ -477,7 +511,8 @@ class AmazonOrders:
|
|
|
477
511
|
from the count Amazon shows on the history page. The total is ``None`` if Amazon doesn't show one, or if
|
|
478
512
|
``order_filter`` is provided, since Amazon's count may not reflect it.
|
|
479
513
|
:param family_member_id: The ID of an Amazon Household member whose Orders to get, such as a linked Teen
|
|
480
|
-
account, from :
|
|
514
|
+
account, from :attr:`~amazonorders.orders.Orders.household_members`. An account in an Amazon
|
|
515
|
+
Household gets the whole household's Orders by default; pass ``"self"`` for only its own.
|
|
481
516
|
:return: A list of the requested Orders.
|
|
482
517
|
"""
|
|
483
518
|
if not self.amazon_session.is_authenticated:
|
|
@@ -486,10 +521,10 @@ class AmazonOrders:
|
|
|
486
521
|
next_page = self._order_history_url(year, start_index, time_filter, order_filter, family_member_id)
|
|
487
522
|
current_index = int(start_index) if start_index else 0
|
|
488
523
|
|
|
489
|
-
orders, _, _ = asyncio.run(self._build_orders_async(next_page, keep_paging, full_details, current_index,
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
524
|
+
orders, _, _, _ = asyncio.run(self._build_orders_async(next_page, keep_paging, full_details, current_index,
|
|
525
|
+
progress=progress,
|
|
526
|
+
progress_with_total=progress_with_total,
|
|
527
|
+
total_known=not order_filter))
|
|
493
528
|
return orders
|
|
494
529
|
|
|
495
530
|
def get_orders_page(self,
|
|
@@ -503,10 +538,11 @@ class AmazonOrders:
|
|
|
503
538
|
progress: Optional[Callable[[int], None]] = None,
|
|
504
539
|
progress_with_total: Optional[Callable[[int, Optional[int]], None]] = None,
|
|
505
540
|
family_member_id: Optional[str] = None
|
|
506
|
-
) ->
|
|
541
|
+
) -> Orders:
|
|
507
542
|
"""
|
|
508
|
-
Get a batch of the Amazon Order history for a given time period, with
|
|
509
|
-
Takes the same filters as
|
|
543
|
+
Get a batch of the Amazon Order history for a given time period, with the Amazon Household members whose
|
|
544
|
+
Orders the account can view and a cursor to fetch the next batch. Takes the same filters as
|
|
545
|
+
:func:`get_orders`.
|
|
510
546
|
|
|
511
547
|
:param year: The year for which to get history. Ignored if ``time_filter`` or ``cursor`` is provided.
|
|
512
548
|
Defaults to the current year if neither ``year`` nor ``time_filter`` is specified.
|
|
@@ -528,7 +564,7 @@ class AmazonOrders:
|
|
|
528
564
|
batch, as in :func:`get_orders`, and at most ``limit`` until the batch ends.
|
|
529
565
|
:param family_member_id: The ID of an Amazon Household member whose Orders to get, as in
|
|
530
566
|
:func:`get_orders`. Ignored if ``cursor`` is provided.
|
|
531
|
-
:return: The batch of Orders, and the cursor for the next one.
|
|
567
|
+
:return: The batch of Orders, the Amazon Household members, and the cursor for the next one.
|
|
532
568
|
"""
|
|
533
569
|
if not self.amazon_session.is_authenticated:
|
|
534
570
|
raise AmazonOrdersError("Call AmazonSession.login() to authenticate first.")
|
|
@@ -545,7 +581,7 @@ class AmazonOrders:
|
|
|
545
581
|
family_member_id)
|
|
546
582
|
current_index = int(start_index) if start_index else 0
|
|
547
583
|
|
|
548
|
-
orders, next_page, current_index = asyncio.run(
|
|
584
|
+
orders, next_page, current_index, household_members = asyncio.run(
|
|
549
585
|
self._build_orders_async(next_page, True, full_details, current_index, limit, progress,
|
|
550
586
|
progress_with_total, not order_filter))
|
|
551
587
|
next_cursor = {
|
|
@@ -555,7 +591,7 @@ class AmazonOrders:
|
|
|
555
591
|
**({"family_member_id": family_member_id} if family_member_id else {}),
|
|
556
592
|
} if next_page else None
|
|
557
593
|
|
|
558
|
-
return
|
|
594
|
+
return Orders(orders, next_cursor, household_members)
|
|
559
595
|
|
|
560
596
|
def _history_filter_value(self,
|
|
561
597
|
year: Optional[int],
|
|
@@ -872,13 +908,16 @@ class AmazonOrders:
|
|
|
872
908
|
progress: Optional[Callable[[int], None]] = None,
|
|
873
909
|
progress_with_total: Optional[Callable[[int, Optional[int]], None]] = None,
|
|
874
910
|
total_known: bool = False
|
|
875
|
-
) -> Tuple[List[Order], Optional[str], int]:
|
|
911
|
+
) -> Tuple[List[Order], Optional[str], int, List[FamilyMember]]:
|
|
876
912
|
order_builds: List[Tuple[Tag, int]] = []
|
|
913
|
+
household_members: List[FamilyMember] = []
|
|
877
914
|
start_index = current_index
|
|
878
915
|
orders_before_start_on_page = current_index % self.config.constants.ORDER_HISTORY_PAGE_SIZE
|
|
879
916
|
|
|
880
917
|
while next_page and (limit is None or len(order_builds) < limit):
|
|
881
918
|
page_response = self._fetch_orders_page(next_page, current_index)
|
|
919
|
+
if not household_members:
|
|
920
|
+
household_members = self._parse_family_members(page_response.parsed, self.config)
|
|
882
921
|
|
|
883
922
|
order_tags = self._parse_orders(page_response.parsed, self.config,
|
|
884
923
|
current_index - orders_before_start_on_page)
|
|
@@ -916,7 +955,7 @@ class AmazonOrders:
|
|
|
916
955
|
bool(next_page)))
|
|
917
956
|
|
|
918
957
|
if not order_builds:
|
|
919
|
-
return [], next_page, current_index
|
|
958
|
+
return [], next_page, current_index, household_members
|
|
920
959
|
|
|
921
960
|
loop = asyncio.get_running_loop()
|
|
922
961
|
with concurrent.futures.ThreadPoolExecutor(max_workers=self.config.thread_pool_size) as pool:
|
|
@@ -927,7 +966,7 @@ class AmazonOrders:
|
|
|
927
966
|
except BaseException:
|
|
928
967
|
pool.shutdown(cancel_futures=True)
|
|
929
968
|
raise
|
|
930
|
-
return orders, next_page, current_index
|
|
969
|
+
return orders, next_page, current_index, household_members
|
|
931
970
|
|
|
932
971
|
@staticmethod
|
|
933
972
|
def _expected_total(order_count: Optional[int],
|
|
@@ -127,7 +127,9 @@ class Selectors:
|
|
|
127
127
|
# CSS selectors for Amazon Household family members
|
|
128
128
|
##########################################################################
|
|
129
129
|
|
|
130
|
-
FAMILY_MEMBER_ENTITY_SELECTOR = ["select#
|
|
130
|
+
FAMILY_MEMBER_ENTITY_SELECTOR = ["select#household-filter option[value]:not([value=''], [value='self'])",
|
|
131
|
+
"select[name='householdMembers'] option[value]:not([value=''], [value='self'])",
|
|
132
|
+
"select#family-member-filter option[value]:not([value=''])",
|
|
131
133
|
"select[name='familyMemberId'] option[value]:not([value=''])"]
|
|
132
134
|
FAMILY_MEMBER_ID_ATTR = "value"
|
|
133
135
|
FIELD_FAMILY_MEMBER_NAME_SUFFIX = "'s Orders"
|
|
@@ -297,6 +297,35 @@ class AmazonSession:
|
|
|
297
297
|
return False
|
|
298
298
|
return True
|
|
299
299
|
|
|
300
|
+
def check_session(self,
|
|
301
|
+
verify: bool = False) -> bool:
|
|
302
|
+
"""
|
|
303
|
+
Check if a persisted session exists, meaning requests can be made without needing to provide credentials.
|
|
304
|
+
|
|
305
|
+
A session's cookies outlive it, so pass ``verify`` to also ask Amazon whether it still accepts the session.
|
|
306
|
+
This requests the Order history and checks it with :func:`check_response`.
|
|
307
|
+
|
|
308
|
+
:param verify: ``True`` to also ask Amazon whether it still accepts the persisted session.
|
|
309
|
+
:return: ``True`` if a persisted session exists and, with ``verify``, Amazon accepts it. ``False`` if none
|
|
310
|
+
exists or, with ``verify``, Amazon redirected to login, so the session needs to sign in again.
|
|
311
|
+
:raises AmazonOrdersError: With ``verify``, if Amazon's response wasn't ``ok``, which says nothing about the
|
|
312
|
+
session.
|
|
313
|
+
"""
|
|
314
|
+
if not self.auth_cookies_stored():
|
|
315
|
+
return False
|
|
316
|
+
if not verify:
|
|
317
|
+
return True
|
|
318
|
+
|
|
319
|
+
logger.debug("Asking Amazon whether it still accepts the persisted session ...")
|
|
320
|
+
|
|
321
|
+
orders_response = self.get(self.config.constants.ORDER_HISTORY_URL)
|
|
322
|
+
try:
|
|
323
|
+
self.check_response(orders_response)
|
|
324
|
+
except AmazonOrdersAuthRedirectError:
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
return True
|
|
328
|
+
|
|
300
329
|
def login(self) -> None:
|
|
301
330
|
"""
|
|
302
331
|
Execute an Amazon login process. This will include the sign-in page, and may also include OTP (if 2FA is
|
|
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
|
|
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
|