Initial commit (clean, ignores in place)

This commit is contained in:
2025-08-12 01:13:41 +01:00
commit c74790b014
26 changed files with 2331 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
import os
import tempfile
import unittest
from openpyxl import load_workbook
from mileage_logger.ingest.semantic_reader import load_place_visits
from mileage_logger.logic.detect_itinerary import SiteConfig, detect_itinerary
from mileage_logger.distance.resolve import DistanceResolver
from mileage_logger.export.excel_writer import build_monthly_rows, write_monthly_workbook
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures", "semantic")
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "..", "config", "sites.yml")
ROUTES_PATH = os.path.join(os.path.dirname(__file__), "data", "routes_golden.csv")
class TestExcelWriter(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.site_config = SiteConfig.from_yaml(CONFIG_PATH)
def _build_workbook(self, fixture_file: str) -> str:
visits = load_place_visits(os.path.join(FIXTURES_DIR, fixture_file))
hops = detect_itinerary(visits, self.site_config)
resolver = DistanceResolver(route_csv_path=ROUTES_PATH)
rows_by_month = build_monthly_rows(hops, self.site_config, resolver)
# Write to a temporary file
fd, path = tempfile.mkstemp(suffix=".xlsx")
os.close(fd)
write_monthly_workbook(rows_by_month, path)
return path
def test_excel_layout_simple(self):
# Build workbook for the simple fixture
path = self._build_workbook("2025-08-08.one_day_simple.json")
try:
wb = load_workbook(path)
# Determine expected month from the first hop date
visits = load_place_visits(os.path.join(FIXTURES_DIR, "2025-08-08.one_day_simple.json"))
hops = detect_itinerary(visits, self.site_config)
if not hops:
self.fail("No hops detected for simple fixture")
expected_month = hops[0].date.strftime("%Y-%m")
self.assertIn(expected_month, wb.sheetnames)
ws = wb[expected_month]
rows = list(ws.iter_rows(values_only=True))
# number of hops + header
self.assertEqual(len(rows), len(hops) + 1)
header = rows[0]
expected_header = ("Date", "Purpose", "Miles", "Vehicle", "Job Role", "From", "To", "Notes")
self.assertEqual(header, expected_header)
# Validate each hop row
resolver = DistanceResolver(route_csv_path=ROUTES_PATH)
for i, hop in enumerate(hops, start=1):
row = rows[i]
origin_site = self.site_config.by_canonical[hop.origin]
dest_site = self.site_config.by_canonical[hop.destination]
dist = resolver.resolve(hop.origin, hop.destination, (origin_site.lat, origin_site.lon), (dest_site.lat, dest_site.lon))
self.assertEqual(row[0], hop.date.isoformat())
expected_purpose = f"Travel from {origin_site.label} to {dest_site.label} {dist:.1f}mi"
self.assertEqual(row[1], expected_purpose)
self.assertAlmostEqual(float(row[2]), dist, places=1)
self.assertEqual(row[3], resolver.vehicle_label)
self.assertEqual(row[4], resolver.job_role)
self.assertEqual(row[5], origin_site.label)
self.assertEqual(row[6], dest_site.label)
# Notes may be returned as None when reading from Excel
self.assertIn(row[7] or "", ["", None])
finally:
os.unlink(path)
def test_cross_midnight_sheet_rows(self):
path = self._build_workbook("cross_midnight.json")
try:
wb = load_workbook(path)
# Should still be month 2025-08
self.assertIn("2025-08", wb.sheetnames)
ws = wb["2025-08"]
rows = list(ws.iter_rows(values_only=True))
# two hops -> 3 rows including header
self.assertEqual(len(rows), 3)
# Dates should span two days
dates = [r[0] for r in rows[1:]]
self.assertEqual(dates, ["2025-08-08", "2025-08-09"])
finally:
os.unlink(path)
if __name__ == '__main__':
unittest.main()