Coverage for amazonorders/cli.py: 81.10%
127 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-29 14:37 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-29 14:37 +0000
1import datetime
2import logging
3from typing import Any, Optional
5import click
6from click.core import Context
8from amazonorders.conf import DEFAULT_OUTPUT_DIR
9from amazonorders.exception import AmazonOrdersError
10from amazonorders.orders import AmazonOrders
11from amazonorders.session import AmazonSession, IODefault
13__author__ = "Alex Laird"
14__copyright__ = "Copyright 2024, Alex Laird"
15__version__ = "1.0.7"
17logger = logging.getLogger("amazonorders")
20class IOClick(IODefault):
21 def echo(self,
22 msg: str,
23 fg: Optional[str] = None,
24 **kwargs: Any):
25 click.secho(msg, fg=fg)
27 def prompt(self,
28 msg: str,
29 type: str = None,
30 **kwargs: Any):
31 return click.prompt(msg, type=type)
34@click.group()
35@click.option('--username', help="An Amazon username.")
36@click.option('--password', help="An Amazon password.")
37@click.option('--debug', is_flag=True, default=False, help="Enable debugging and send output to command line.")
38@click.option('--max-auth-attempts', default=10,
39 help="Will continue in the login auth loop this many times (successes and failures).")
40@click.option('--output-dir', default=DEFAULT_OUTPUT_DIR,
41 help="The directory where any output files should be produced.")
42@click.pass_context
43def amazon_orders_cli(ctx: Context,
44 **kwargs: Any):
45 """
46 amazon-orders is an unofficial library that provides a command line interface alongside a programmatic API that
47 can be used to interact with Amazon.com's consumer-facing website.
49 This works by parsing website data from Amazon.com. A nightly build validates functionality to ensure its
50 stability, but as Amazon provides no official API to use, this package may break at any time. This
51 package only supports the English version of the website.
53 Documentation can be found at https://amazon-orders.readthedocs.io.
55 Session data is persisted between requests and interactions with the CLI, minimizing the need to reauthenticate
56 after a successful login attempt.
57 """
58 _print_banner()
60 ctx.ensure_object(dict)
61 for key, value in kwargs.items():
62 if value:
63 ctx.obj[key] = value
65 if kwargs["debug"]:
66 logger.setLevel(logging.DEBUG)
67 logger.addHandler(logging.StreamHandler())
69 username = kwargs.get("username")
70 password = kwargs.get("password")
72 amazon_session = AmazonSession(username,
73 password,
74 debug=kwargs["debug"],
75 io=IOClick(),
76 max_auth_attempts=kwargs["max_auth_attempts"],
77 output_dir=kwargs["output_dir"])
79 ctx.obj["amazon_session"] = amazon_session
82@amazon_orders_cli.command()
83@click.pass_context
84@click.option('--year', default=datetime.date.today().year,
85 help="The year for which to get order history, defaults to the current year.")
86@click.option('--start-index', help="Retrieve the single page of history at the given index.")
87@click.option('--full-details', is_flag=True, default=False,
88 help="Retrieve the full details for each order in the history.")
89def history(ctx: Context,
90 **kwargs: Any):
91 """
92 Retrieve Amazon order history for a given year.
93 """
94 amazon_session = ctx.obj["amazon_session"]
96 year = kwargs["year"]
97 start_index = kwargs["start_index"]
98 full_details = kwargs["full_details"]
100 click.echo("""-----------------------------------------------------------------------
101Order History for {}{}{}
102-----------------------------------------------------------------------\n""".format(year,
103 ", startIndex={}, one page".format(
104 start_index) if start_index else ", all pages",
105 ", with full details" if full_details else ""))
107 click.echo("Info: This might take a minute ...\n")
109 try:
110 _authenticate(ctx, amazon_session)
112 amazon_orders = AmazonOrders(amazon_session,
113 debug=amazon_session.debug,
114 output_dir=ctx.obj["output_dir"])
116 orders = amazon_orders.get_order_history(year=kwargs["year"],
117 start_index=kwargs["start_index"],
118 full_details=kwargs["full_details"], )
120 for order in orders:
121 click.echo("{}\n".format(_order_output(order)))
122 except AmazonOrdersError as e:
123 logger.debug("An error occurred.", exc_info=True)
124 ctx.fail(str(e))
127@amazon_orders_cli.command()
128@click.pass_context
129@click.argument("order_id")
130def order(ctx: Context,
131 order_id: str):
132 """
133 Retrieve the full details for the given Amazon order ID.
134 """
135 amazon_session = ctx.obj["amazon_session"]
137 try:
138 _authenticate(ctx, amazon_session)
140 amazon_orders = AmazonOrders(amazon_session,
141 debug=amazon_session.debug,
142 output_dir=ctx.obj["output_dir"])
144 order = amazon_orders.get_order(order_id)
146 click.echo("{}\n".format(_order_output(order)))
147 except AmazonOrdersError as e:
148 logger.debug("An error occurred.", exc_info=True)
149 ctx.fail(str(e))
152@amazon_orders_cli.command(short_help="Check if persisted session exists.")
153@click.pass_context
154def check_session(ctx: Context):
155 """
156 Check if a persisted session exists, meaning commands can be called without needing to provide credentials.
157 """
158 amazon_session = ctx.obj["amazon_session"]
159 if amazon_session.auth_cookies_stored():
160 click.echo("Info: A persisted session exists.\n")
161 else:
162 click.echo("Info: No persisted session exists.\n")
165@amazon_orders_cli.command()
166@click.pass_context
167def logout(ctx: Context):
168 """
169 Logout of existing Amazon sessions and clear cookies.
170 """
171 amazon_session = ctx.obj["amazon_session"]
172 amazon_session.logout()
174 click.echo("Info: Successfully logged out of the Amazon session.\n")
177@amazon_orders_cli.command()
178def version():
179 """
180 Get the package version.
181 """
182 click.echo("Version: {}\n".format(__version__))
185def _print_banner():
186 click.echo("""
187=======================================================================
188 ___ _____ _
189 / _ \ | _ | | |
190/ /_\ \_ __ ___ __ _ _______ _ __ | | | |_ __ __| | ___ _ __ ___
191| _ | '_ ` _ \ / _` |_ / _ \| '_ \ | | | | '__/ _` |/ _ \ '__/ __|
192| | | | | | | | | (_| |/ / (_) | | | | \ \_/ / | | (_| | __/ | \__ \\
193\_| |_/_| |_| |_|\__,_/___\___/|_| |_| \___/|_| \__,_|\___|_| |___/
194=======================================================================\n""")
197def _authenticate(ctx: Context,
198 amazon_session: AmazonSession):
199 if not amazon_session.username and not amazon_session.password:
200 click.echo(ctx.get_help())
202 ctx.fail("Amazon --username and --password must be provided, since no previous session was found.")
204 if amazon_session.auth_cookies_stored():
205 if amazon_session.username or amazon_session.password:
206 click.echo("Info: You've provided --username and --password, but because a previous session still exists,"
207 "that is being ignored. If you would like to reauthenticate, call the `logout` command first.\n")
209 amazon_session.login()
212def _order_output(order):
213 order_str = """-----------------------------------------------------------------------
214Order #{}
215-----------------------------------------------------------------------""".format(order.order_number)
217 order_str += "\n Shipments: {}".format(order.shipments)
218 order_str += "\n Order Details Link: {}".format(order.order_details_link)
219 order_str += "\n Grand Total: ${:,.2f}".format(order.grand_total)
220 order_str += "\n Order Placed Date: {}".format(order.order_placed_date)
221 order_str += "\n {}".format(order.recipient)
222 if order.payment_method:
223 order_str += "\n Payment Method: {}".format(order.payment_method)
224 if order.payment_method_last_4:
225 order_str += "\n Payment Method Last 4: {}".format(order.payment_method_last_4)
226 if order.subtotal:
227 order_str += "\n Subtotal: ${:,.2f}".format(order.subtotal)
228 if order.shipping_total:
229 order_str += "\n Shipping Total: ${:,.2f}".format(order.shipping_total)
230 if order.subscription_discount:
231 order_str += "\n Subscription Discount: ${:,.2f}".format(order.subscription_discount)
232 if order.total_before_tax:
233 order_str += "\n Total Before Tax: ${:,.2f}".format(order.total_before_tax)
234 if order.estimated_tax:
235 order_str += "\n Estimated Tax: ${:,.2f}".format(order.estimated_tax)
236 if order.refund_total:
237 order_str += "\n Refund Total: ${:,.2f}".format(order.refund_total)
238 if order.order_shipped_date:
239 order_str += "\n Order Shipped Date: {}".format(order.order_shipped_date)
240 if order.refund_completed_date:
241 order_str += "\n Refund Completed Date: {}".format(order.refund_completed_date)
243 order_str += "\n-----------------------------------------------------------------------"
245 return order_str
248if __name__ == "__main__":
249 amazon_orders_cli(obj={})