Various improvements
This commit is contained in:
parent
b7d95f26ec
commit
442ae98463
3 changed files with 100 additions and 3 deletions
|
|
@ -37,6 +37,8 @@ class BookingConfig:
|
|||
booking_type: str
|
||||
yaml_filename: str
|
||||
spec_heading: str
|
||||
start_field: str
|
||||
excluded_top_level_keys: tuple[str, ...]
|
||||
json_key: str = "booking"
|
||||
|
||||
|
||||
|
|
@ -45,11 +47,22 @@ BOOKING_CONFIGS: dict[str, BookingConfig] = {
|
|||
booking_type="flight",
|
||||
yaml_filename="flights.yaml",
|
||||
spec_heading="flights.yaml",
|
||||
start_field="depart",
|
||||
excluded_top_level_keys=("trip",),
|
||||
),
|
||||
"train": BookingConfig(
|
||||
booking_type="train",
|
||||
yaml_filename="trains.yaml",
|
||||
spec_heading="trains.yaml",
|
||||
start_field="depart",
|
||||
excluded_top_level_keys=("trip",),
|
||||
),
|
||||
"accommodation": BookingConfig(
|
||||
booking_type="accommodation",
|
||||
yaml_filename="accommodation.yaml",
|
||||
spec_heading="accommodation.yaml",
|
||||
start_field="from",
|
||||
excluded_top_level_keys=("trip", "latitude", "longitude"),
|
||||
),
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +122,7 @@ def build_prompt(
|
|||
bookings = current_bookings
|
||||
if bookings is None:
|
||||
bookings = read_existing_bookings(config)
|
||||
excluded_keys = ", ".join(f'"{key}"' for key in config.excluded_top_level_keys)
|
||||
|
||||
return f"""
|
||||
I keep a record of all my {config.booking_type} bookings in a YAML file.
|
||||
|
|
@ -131,7 +145,7 @@ Rules:
|
|||
- Wrap the response in a JSON object with a single key "{config.json_key}" that
|
||||
contains the booking in YAML.
|
||||
- The value of "{config.json_key}" must be YAML text, not JSON.
|
||||
- Exclude the top-level "trip" key from the YAML.
|
||||
- Exclude these top-level keys from the YAML: {excluded_keys}.
|
||||
- Do not invent details that are not present in the booking text.
|
||||
- Quote prices and identifiers that might otherwise be parsed as numbers.
|
||||
|
||||
|
|
@ -218,7 +232,7 @@ def first_departure(booking: dict[str, typing.Any], config: BookingConfig) -> da
|
|||
assert isinstance(first_flight, dict)
|
||||
return datetime_from_yaml_value(first_flight["depart"])
|
||||
|
||||
return datetime_from_yaml_value(booking["depart"])
|
||||
return datetime_from_yaml_value(booking[config.start_field])
|
||||
|
||||
|
||||
def comparable_departure(
|
||||
|
|
@ -246,6 +260,13 @@ def trip_key_position(booking: dict[str, typing.Any], config: BookingConfig) ->
|
|||
return keys.index("booking_reference") + 1
|
||||
return 0
|
||||
|
||||
if config.booking_type == "accommodation":
|
||||
if "country" in booking:
|
||||
return keys.index("country") + 1
|
||||
if "location" in booking:
|
||||
return keys.index("location") + 1
|
||||
return 0
|
||||
|
||||
if "to" in booking:
|
||||
return keys.index("to") + 1
|
||||
if "from" in booking:
|
||||
|
|
@ -455,3 +476,8 @@ def train_main(argv: list[str] | None = None) -> int:
|
|||
def flight_main(argv: list[str] | None = None) -> int:
|
||||
"""CLI entrypoint for flight booking YAML generation."""
|
||||
return main_for_type("flight", argv)
|
||||
|
||||
|
||||
def accommodation_main(argv: list[str] | None = None) -> int:
|
||||
"""CLI entrypoint for accommodation booking YAML generation."""
|
||||
return main_for_type("accommodation", argv)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ This document describes the YAML files read from `../personal-data/`. It is inte
|
|||
- Currencies must be in `config.CURRENCIES` or `GBP`.
|
||||
- Travel and trip-related entries are grouped by the `trip` date. That date should match an entry in `trips.yaml` when a named trip is needed, but trip groups can also be created from travel/accommodation/conference entries.
|
||||
- Keep chronological files sorted by their natural start field. `validate_yaml.py` checks ordering for trips, flights, trains, ferries, conferences, and accommodation.
|
||||
- Preserve the existing whitespace style. Long top-level list files such as `accommodation.yaml`, `buses.yaml`, `car_journeys.yaml`, `coaches.yaml`, `conferences.yaml`, `ferries.yaml`, `flights.yaml`, `stations.yaml`, `trains.yaml`, and `trips.yaml` use one blank line between top-level items. Mapping files such as `airports.yaml` do not use this list-item spacing.
|
||||
- Coordinates are `latitude` then `longitude`, both numeric.
|
||||
|
||||
## Cross-File References
|
||||
|
|
|
|||
|
|
@ -40,10 +40,25 @@ def test_build_prompt_uses_spec_and_keeps_trip_exclusion() -> None:
|
|||
|
||||
assert "Use this YAML format specification" in prompt
|
||||
assert "## `trains.yaml`" in prompt
|
||||
assert 'Exclude the top-level "trip" key' in prompt
|
||||
assert 'Exclude these top-level keys from the YAML: "trip"' in prompt
|
||||
assert "Eurostar booking details" in prompt
|
||||
|
||||
|
||||
def test_build_prompt_for_accommodation_excludes_coordinates() -> None:
|
||||
"""Accommodation prompt should exclude trip and coordinate fields."""
|
||||
prompt = generate_booking_yaml.build_prompt(
|
||||
"Airbnb booking details",
|
||||
generate_booking_yaml.BOOKING_CONFIGS["accommodation"],
|
||||
current_bookings="- type: airbnb\n",
|
||||
)
|
||||
|
||||
assert "## `accommodation.yaml`" in prompt
|
||||
assert (
|
||||
'Exclude these top-level keys from the YAML: "trip", "latitude", "longitude"'
|
||||
in prompt
|
||||
)
|
||||
|
||||
|
||||
def test_booking_text_from_args_fetches_url(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""URL arguments should be fetched instead of reading stdin."""
|
||||
monkeypatch.setattr(
|
||||
|
|
@ -185,3 +200,58 @@ flights:
|
|||
assert [item["booking_reference"] for item in written] == ["OLD", "MID", "NEWER"]
|
||||
assert written[1]["trip"] == date(2026, 2, 6)
|
||||
assert list(written[1]).index("trip") == 1
|
||||
|
||||
|
||||
def test_import_accommodation_booking_adds_trip_and_inserts_chronologically(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Generated accommodation should be written into accommodation.yaml in order."""
|
||||
(tmp_path / "accommodation.yaml").write_text("""---
|
||||
- type: hotel
|
||||
name: Earlier Hotel
|
||||
location: Brussels
|
||||
country: be
|
||||
trip: 2026-02-01
|
||||
from: 2026-02-01 15:00:00+01:00
|
||||
to: 2026-02-02 11:00:00+01:00
|
||||
|
||||
- type: hotel
|
||||
name: Later Hotel
|
||||
location: Paris
|
||||
country: fr
|
||||
trip: 2026-02-10
|
||||
from: 2026-02-10 15:00:00+01:00
|
||||
to: 2026-02-11 11:00:00+01:00
|
||||
""")
|
||||
monkeypatch.setattr(
|
||||
generate_booking_yaml,
|
||||
"matching_trip_date",
|
||||
lambda depart, data_dir: date(2026, 2, 6),
|
||||
)
|
||||
|
||||
count = generate_booking_yaml.import_booking_yaml(
|
||||
"""type: airbnb
|
||||
name: Paris Airbnb
|
||||
location: Paris
|
||||
country: fr
|
||||
from: 2026-02-06 15:00:00+01:00
|
||||
to: 2026-02-09 11:00:00+01:00
|
||||
price: '300.00'
|
||||
currency: EUR
|
||||
""",
|
||||
generate_booking_yaml.BOOKING_CONFIGS["accommodation"],
|
||||
data_dir=tmp_path,
|
||||
)
|
||||
|
||||
text = (tmp_path / "accommodation.yaml").read_text()
|
||||
written = yaml.safe_load(text)
|
||||
assert count == 1
|
||||
assert [item["name"] for item in written] == [
|
||||
"Earlier Hotel",
|
||||
"Paris Airbnb",
|
||||
"Later Hotel",
|
||||
]
|
||||
assert written[1]["trip"] == date(2026, 2, 6)
|
||||
assert list(written[1]).index("trip") == 4
|
||||
assert "\n\n- type: airbnb\n" in text
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue