Files
mileage-reports/mileage_rate.py
John Lancaster 29c4fb7049 initial commit
2025-11-17 12:00:17 -06:00

97 lines
2.8 KiB
Python

# /// script
# requires-python = ">=3.12"
# dependencies = ["typer", "rich", "xlwings"]
# ///
from contextlib import contextmanager
from pathlib import Path
from typing import Annotated
import xlwings as xw
import typer
from rich.console import Console
console = Console()
def get_totals(book: xw.Book):
for sheet in book.sheets:
if sheet.name.endswith("Rate"):
continue
try:
total = float(sheet.range("S3").expand("down").value[-1])
except Exception as e:
console.print(f"[red]Error reading total from sheet {sheet.name}: {e}[/red]")
console.print_exception(show_locals=False)
else:
yield sheet.name, total
def get_grand_total(book: xw.Book):
totals = dict(get_totals(book))
grand_total = sum(totals.values())
return grand_total
@contextmanager
def switch_values(book: xw.Book):
og_vals = book.sheets['GSA Rate'].range("B2").expand("down").value
try:
book.sheets['GSA Rate'].range("B2").expand("down").value = 0.70
yield
finally:
book.sheets['GSA Rate'].range("B2").expand("down").options(transpose=True).value = og_vals
def get_diff(book: xw.Book):
book.sheets['GSA Rate'].range("B2:B10").value = 0.67
initial = get_grand_total(book)
with switch_values(book):
fixed = get_grand_total(book)
diff = round(fixed - initial, 2)
return diff
def get_all_diffs(base: Path):
reports = [f for f in base.glob('*Mileage*.xlsx') if f.is_file() and not f.stem.startswith('~$')]
console.print(f"Found {len(reports)} report(s).")
for r in reports:
with console.status(f'Processing [bold blue]{r.name}[/bold blue]'):
console.print('Opening workbook...')
book = xw.Book(r)
try:
console.print('Calculating totals...')
diff = get_diff(book)
yield r.name, diff
console.print(f"[cyan]Report:[/] {r.name}")
console.print(f" Difference after rate change: [bold green]${diff:.2f}[/]")
except Exception as e:
console.print(f"[red]Error processing {r.name}: {e}[/red]")
finally:
book.close()
def main(
directory: Annotated[
Path,
typer.Argument(
exists=True,
file_okay=False,
dir_okay=True,
readable=True,
writable=True,
resolve_path=True
)
] = None) -> None:
console.print(f"Processing files in directory: {directory}")
results = dict(get_all_diffs(directory))
with Path('results.csv').open('w', encoding='utf-8') as f:
f.write("Report,Difference\n")
for report, diff in results.items():
f.write(f'{report},{diff}\n')
if __name__ == "__main__":
typer.run(main)