initial commit
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Python-generated files
|
||||||
|
__pycache__/
|
||||||
|
*.py[oc]
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
wheels/
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
96
mileage_rate.py
Normal file
96
mileage_rate.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# /// 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)
|
||||||
Reference in New Issue
Block a user