def reconcile_new(config, args): reconciliation_uploader = ReconciliationUploader(config) tracking_output = TrackingOutput(config) trackings = tracking_output.get_existing_trackings() reconcilable_trackings = [t for t in trackings if t.reconcile] # start from scratch all_clusters = [] clusters.update_clusters(all_clusters, reconcilable_trackings) order_info_retriever = OrderInfoRetriever(config) fill_billed_costs('Fetching order costs', all_clusters, order_info_retriever, True) all_clusters = clusters.merge_orders(all_clusters) fill_billed_costs('Filling merged order costs', all_clusters, order_info_retriever, False) # add manual PO entries (and only manual ones) reconciliation_uploader.override_pos_and_costs(all_clusters) driver_creator = DriverCreator() group_site_manager = GroupSiteManager(config, driver_creator) trackings_to_cost, po_to_cost = get_new_tracking_pos_costs_maps( config, group_site_manager, args) clusters_by_tracking = map_clusters_by_tracking(all_clusters) merge_by_trackings_tuples(clusters_by_tracking, trackings_to_cost, all_clusters) fill_costs_new(clusters_by_tracking, trackings_to_cost, po_to_cost, args) fill_cancellations(all_clusters, config) reconciliation_uploader.download_upload_clusters_new(all_clusters)
def run_add(config): print("Add tracking to existing tracking/order cluster.") existing_tracking_num = get_required( "Enter a tracking number of the existing cluster: ") tracking_output = TrackingOutput(config) tracking = tracking_output.get_tracking(existing_tracking_num) if not tracking: print("Error: Tracking does not exist. Aborting.") return print("Existing tracking data is:") print(tracking) new_tracking_num = get_optional( "Enter new tracking number (or blank to abort): ") if not new_tracking_num: print("Aborting.") return tracking.tracking_number = new_tracking_num print("New tracking data is:") print(tracking) submit = get_required_from_options("Save?", ['y', 'n']) if submit: tracking_output.save_trackings([tracking]) print("Saved.") else: print("Cancelled.")
def run_auto(config, args): if not args.tracking or not args.order or not args.group: raise Exception( 'Must provide tracking, order, and group if doing auto') orders = set() orders.add(args.order) tracking = Tracking(args.tracking, args.group, orders, '', '', '', TODAY, 0.0, '', '') print(tracking) tracking_output = TrackingOutput(config) tracking_output.save_trackings([tracking], overwrite=True)
def filter_orders(orders_list, config): tracking_output = TrackingOutput(config) seen_order_ids = set() for tracking in tracking_output.get_existing_trackings(): seen_order_ids.update(tracking.order_ids) cancelled_items_retriever = CancelledItemsRetriever(config) cancellations_by_order = cancelled_items_retriever.get_cancelled_items() result = [] for order in orders_list: if order.order_id not in seen_order_ids and order.order_id not in cancellations_by_order: result.append(order) return result
def run_new(config): print("Manual input of Tracking object.") print("Optional fields will display a default in brackets if one exists.") print("") tracking_output = TrackingOutput(config) tracking_number = get_required("Tracking number: ") tracking = tracking_output.get_tracking(tracking_number) if tracking: print("This tracking number already exists:") print(tracking) print("Adding new order(s) to the existing tracking number.") orders_to_costs = input_orders() if tracking: order_ids_set = set(tracking.order_ids) order_ids_set.update(orders_to_costs.keys()) tracking.order_ids = list(order_ids_set) tracking.price = '' # Zero out price for reconcile to fix later. else: ship_date = get_optional_with_default( "Optional ship date, formatted YYYY-MM-DD [%s]: " % TODAY, TODAY) group = get_required("Group, e.g. mysbuyinggroup: ") email = get_optional("Optional email address: ") order_url = get_optional("Optional order URL: ") merchant = get_optional("Optional merchant: ") description = get_optional("Optional item descriptions: ") tracking = Tracking(tracking_number, group, set(orders_to_costs.keys()), '', email, order_url, ship_date, 0.0, description, merchant) print("Resulting tracking object: ") print(tracking) print("Order to cost map: ") print(orders_to_costs) submit = get_required_from_options("Submit? 'y' to submit, 'n' to quit: ", ['y', 'n']) if submit == 'y': tracking_output.save_trackings([tracking], overwrite=True) print("Wrote tracking") order_info_retriever = OrderInfoRetriever(config) order_info_retriever.orders_dict.update(orders_to_costs) order_info_retriever.flush() print("Wrote billed amounts") print("This will be picked up on next reconciliation run.") elif submit == 'n': print("Submission cancelled.")
def run_delete(config): print("Manual deletion of Tracking object.") tracking_number = get_required("Tracking number: ") tracking_output = TrackingOutput(config) existing_trackings = tracking_output.get_existing_trackings() found_list = [ tracking for tracking in existing_trackings if tracking.tracking_number == tracking_number ] if found_list: to_delete = found_list[0] print("This is the Tracking object: %s" % to_delete) submit = get_required_from_options( "Are you sure you want to delete this tracking?", ['y', 'n']) if submit == 'y': existing_trackings.remove(to_delete) tracking_output._write_merged(existing_trackings) else: print("Deletion stopped.") else: print("Could not find that tracking number.")
def reconcile_new(config, args): reconciliation_uploader = ReconciliationUploader(config) tracking_output = TrackingOutput(config) trackings = tracking_output.get_existing_trackings() reconcilable_trackings = [t for t in trackings if t.reconcile] # start from scratch all_clusters = [] clusters.update_clusters(all_clusters, reconcilable_trackings) order_info_retriever = OrderInfoRetriever(config) fill_billed_costs('Fetching order costs', all_clusters, order_info_retriever, True) all_clusters = clusters.merge_orders(all_clusters) fill_billed_costs('Filling merged order costs', all_clusters, order_info_retriever, False) # add manual PO entries (and only manual ones) reconciliation_uploader.override_pos_and_costs(all_clusters) driver_creator = DriverCreator() group_site_manager = GroupSiteManager(config, driver_creator) trackings_to_info, po_to_cost = get_new_tracking_pos_costs_maps(config, group_site_manager, args) clusters_by_tracking = map_clusters_by_tracking(all_clusters) merge_by_trackings_tuples(clusters_by_tracking, trackings_to_info, all_clusters) fill_costs_new(clusters_by_tracking, trackings_to_info, po_to_cost, args) fill_cancellations(all_clusters, config) et(config, all_clusters) sheet_id = config['reconciliation']['baseSpreadsheetId'] if args.groups: print("Skipping unknown-tracking upload due to the --groups argument") else: upload_unknown_trackings(sheet_id, set(clusters_by_tracking.keys()), trackings_to_info) reconciliation_uploader.download_upload_clusters_new(all_clusters)
def run_add(config): new_trackings = [] print("Add tracking to existing tracking/order cluster.") tracking_output = TrackingOutput(config) while True: existing_tracking_num = get_optional( "Enter the tracking number of the existing cluster (or blank to finish): " ) if not existing_tracking_num: break tracking = tracking_output.get_tracking(existing_tracking_num) if not tracking: print("Error: Tracking does not exist. Aborting.") continue print("Existing tracking data is:") print(tracking) new_tracking_num = get_optional( "Enter new tracking number (or blank to abort): ") if not new_tracking_num: print("Aborting.") continue tracking.tracking_number = new_tracking_num print("New tracking data is:") print(tracking) submit = get_required_from_options("Save?", ['y', 'n']) if submit: new_trackings.append(tracking) print("Saved.") else: print("Cancelled.") if len(new_trackings) > 0: print(f"Saving {len(new_trackings)} new tracking(s) and quitting.") tracking_output.save_trackings(new_trackings) else: print("No new trackings to save; quitting.")
def main(): parser = argparse.ArgumentParser(description='Get tracking #s script') parser.add_argument("--seen", action="store_true") parser.add_argument("--days") args, _ = parser.parse_known_args() driver_creator = DriverCreator() config = open_config() email_config = config['email'] email_sender = EmailSender(email_config) print("Retrieving Amazon tracking numbers from email ...") amazon_tracking_retriever = AmazonTrackingRetriever( config, args, driver_creator) try: trackings = amazon_tracking_retriever.get_trackings() except: send_error_email(email_sender, "Error retrieving Amazon emails") raise print("Retrieving Best Buy tracking numbers from email ...") bestbuy_tracking_retriever = BestBuyTrackingRetriever( config, args, driver_creator) try: trackings.update(bestbuy_tracking_retriever.get_trackings()) except: send_error_email(email_sender, "Error retrieving Best Buy emails") raise try: tracking_output = TrackingOutput(config) existing_tracking_nos = set([ t.tracking_number for t in tracking_output.get_existing_trackings() ]) new_tracking_nos = set( trackings.keys()).difference(existing_tracking_nos) print(f"Found {len(new_tracking_nos)} new tracking numbers " f"(out of {len(trackings)} total) from emails.") new_trackings = [trackings[n] for n in new_tracking_nos] # We only need to process new tracking numbers if there are any; # otherwise skip straight to processing existing locally stored data. if new_trackings: try: email_sender.send_email(new_trackings) except Exception as e: # When running --seen, we're often processing a very large number of emails that can # take a long time, and the Tracking Numbers email isn't too important to us (but the # upload to portals/Sheets definitely is). So don't fail after we've been running for # a long time just on account of a failed email. if args.seen: print( f"Email sending failed with error: {str(e)}\n{util.get_traceback_lines()}" ) print("New trackings are:\n" + "\n".join([str(nt) for nt in new_trackings])) print( "Continuing to portal/Sheet upload because email sending is non-essential." ) else: raise e print("Uploading all tracking numbers...") group_site_manager = GroupSiteManager(config, driver_creator) try: group_site_manager.upload(trackings.values()) except: send_error_email(email_sender, "Error uploading tracking numbers") if args.seen: print("Error uploading tracking numbers; skipping.") else: raise reconcilable_trackings = [t for t in new_trackings if t.reconcile] # Also only add new trackings to the sheet print("Adding results to Google Sheets") tracking_uploader = TrackingUploader(config) try: tracking_uploader.upload_trackings(reconcilable_trackings) except: send_error_email(email_sender, "Error uploading to Google Sheets") if args.seen: print("Error uploading to Google Sheets; skipping.") else: raise print("Writing results to file") try: tracking_output.save_trackings(new_trackings) except: send_error_email(email_sender, "Error writing output file") raise print("Done") except: print("Exception thrown after looking at the emails.") if not args.seen: print("Marking all relevant emails as unread to reset.") amazon_tracking_retriever.back_out_of_all() bestbuy_tracking_retriever.back_out_of_all() if not args.seen: print("Marked all as unread.") raise
def main(): parser = argparse.ArgumentParser( description='Importing Amazon reports from CSV or Drive') parser.add_argument("--personal", "-p", action="store_true", help="Use the personal CSV format") parser.add_argument("globs", nargs="*") args, _ = parser.parse_known_args() from_row_function = from_personal_row if args.personal else from_amazon_row all_trackings = [] if args.globs: for glob_input in args.globs: files = glob.glob(glob_input) for file in files: all_trackings.extend( read_trackings_from_file(file, from_row_function)) else: sheet_id = get_required("Enter Google Sheet ID: ") tab_name = get_required("Enter the name of the tab within the sheet: ") objects_to_sheet = ObjectsToSheet() all_trackings.extend( objects_to_sheet.download_from_sheet(from_amazon_row, sheet_id, tab_name)) if len(all_trackings) == 0: print("Nothing to import; terminating.") return num_n_a_trackings = len([ ignored for ignored in all_trackings if ignored and ignored.tracking_number == 'N/A' ]) num_empty_trackings = len([ ignored for ignored in all_trackings if ignored and ignored.tracking_number == '' ]) print(f'Skipping {num_n_a_trackings} for N/A tracking column and ' f'{num_empty_trackings} for empty tracking column.') all_trackings = [ tracking for tracking in all_trackings if tracking and tracking.tracking_number != 'N/A' and tracking.tracking_number != '' ] len_non_reconcilable_trackings = len( [t for t in all_trackings if not t.reconcile]) print( f'Skipping {len_non_reconcilable_trackings} non-reconcilable trackings.' ) all_trackings = [t for t in all_trackings if t.reconcile] base_len_trackings = len(all_trackings) all_trackings = dedupe_trackings(all_trackings) print( f'Filtered {base_len_trackings - len(all_trackings)} duplicate trackings from the sheet.' ) print('Uploading trackings to Sheets...') tracking_uploader = TrackingUploader(config) tracking_uploader.upload_trackings(all_trackings) tracking_output = TrackingOutput(config) trackings_before_save = { t.tracking_number for t in tracking_output.get_existing_trackings() } print(f"Number of trackings before: {len(trackings_before_save)}.") print(f"Number imported from report(s): {len(all_trackings)}.") tracking_output.save_trackings(all_trackings) trackings_after_save = { t.tracking_number: t for t in tracking_output.get_existing_trackings() } print(f"Number of trackings after: {len(trackings_after_save)}.") new_trackings = set( trackings_after_save.keys()).difference(trackings_before_save) print(f"Number of new-to-us trackings: {len(new_trackings)}") new_tracking_objects = [trackings_after_save[t] for t in new_trackings] email_config = config['email'] email_sender = EmailSender(email_config) email_sender.send_email(new_tracking_objects) print("Uploading new trackings to the group(s)' site(s)...") group_site_manager = GroupSiteManager(config, DriverCreator()) group_site_manager.upload(new_tracking_objects)
def main(): parser = argparse.ArgumentParser( description='Importing Amazon reports from CSV or Drive') parser.add_argument("globs", nargs="*") args, _ = parser.parse_known_args() all_trackings = [] if args.globs: for glob_input in args.globs: files = glob.glob(glob_input) for file in files: all_trackings.extend(read_trackings_from_file(file)) else: sheet_id = get_required("Enter Google Sheet ID: ") tab_name = get_required("Enter the name of the tab within the sheet: ") objects_to_sheet = ObjectsToSheet() all_trackings.extend( objects_to_sheet.download_from_sheet(from_amazon_row, sheet_id, tab_name)) num_n_a_trackings = len([ ignored for ignored in all_trackings if ignored and ignored.tracking_number == 'N/A' ]) num_empty_trackings = len([ ignored for ignored in all_trackings if ignored and ignored.tracking_number == '' ]) print( f'Skipping {num_n_a_trackings} for n/a tracking column and {num_empty_trackings} for empty tracking column' ) all_trackings = [ tracking for tracking in all_trackings if tracking and tracking.tracking_number != 'N/A' and tracking.tracking_number != '' ] len_non_reconcilable_trackings = len( [t for t in all_trackings if not t.reconcile]) print( f'Skipping {len_non_reconcilable_trackings} non-reconcilable trackings' ) all_trackings = [t for t in all_trackings if t.reconcile] base_len_trackings = len(all_trackings) all_trackings = dedupe_trackings(all_trackings) print( f'Filtered {base_len_trackings - len(all_trackings)} duplicate trackings from the sheet' ) print('Uploading trackings to Sheets...') tracking_uploader = TrackingUploader(config) tracking_uploader.upload_trackings(all_trackings) tracking_output = TrackingOutput(config) print("Number of trackings beforehand: %d" % len(tracking_output.get_existing_trackings())) print("Number from sheet: %d" % len(all_trackings)) tracking_output.save_trackings(all_trackings) print("Number of trackings after: %d" % len(tracking_output.get_existing_trackings())) print("Uploading to the group(s)' site(s)...") group_site_manager = GroupSiteManager(config, DriverCreator()) group_site_manager.upload(all_trackings)