diff --git a/local-app/python-tools/billing/aws_cost_compare.py b/local-app/python-tools/billing/aws_cost_compare.py new file mode 100755 index 00000000..9056892f --- /dev/null +++ b/local-app/python-tools/billing/aws_cost_compare.py @@ -0,0 +1,84 @@ +#!/bin/env python + +import csv +import argparse +import sys + +__version__ = "1.0.2" + +def load_csv_to_dict(filename): + data = {} + try: + with open(filename, mode='r') as f: + reader = csv.DictReader(f) + for row in reader: + service = row['Service'] + if service == 'GRAND TOTAL': + continue + # Extract usage and amortized cost + data[service] = { + 'usage': float(row['Usage Quantity']), + 'cost': float(row['Total (Amortized)']) + } + return data + except Exception as e: + print(f"❌ Error reading {filename}: {e}") + sys.exit(1) + +def compare_costs(file1, file2): + old_data = load_csv_to_dict(file1) + new_data = load_csv_to_dict(file2) + + all_services = set(old_data.keys()).union(set(new_data.keys())) + comparison_rows = [] + total_cost_change = 0.0 + + for svc in all_services: + old = old_data.get(svc, {'usage': 0.0, 'cost': 0.0}) + new = new_data.get(svc, {'usage': 0.0, 'cost': 0.0}) + + usage_diff = new['usage'] - old['usage'] + cost_diff = new['cost'] - old['cost'] + + # Exclude Private Offers from the total calculation logic + if "Private Offer" not in svc: + total_cost_change += cost_diff + + comparison_rows.append({ + 'Service': svc, + 'Usage Change': usage_diff, + 'Cost Change': cost_diff + }) + + # Sort: Largest savings (most negative) at the top, highest increases at the bottom + comparison_rows.sort(key=lambda x: x['Cost Change'], reverse=False) + + output_file = "cost_comparison.csv" + print(f"--- AWS Cost Comparison v{__version__} ---") + print(f"Savings (negative) appear first; increases appear last.") + print(f"Note: 'Private Offer' services excluded from grand total calculation.\n") + + with open(output_file, 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(['Service', 'Usage Change', 'Cost Change']) + + for row in comparison_rows: + writer.writerow([ + row['Service'], + f"{row['Usage Change']:.2f}", + f"{row['Cost Change']:.2f}" + ]) + + # Grand Total line + writer.writerow(['TOTAL', 'NA', f"{total_cost_change:.2f}"]) + + print(f"✅ Comparison saved to {output_file}") + print(f"Filtered Net Change: ${total_cost_change:.2f}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Compare two AWS Cost CSVs (v1.0.2)") + parser.add_argument('old_file', help="Baseline CSV") + parser.add_argument('new_file', help="Comparison CSV") + parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}') + args = parser.parse_args() + compare_costs(args.old_file, args.new_file)