mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-30 18:46:00 +00:00
feat(extract): capture seat, class, platform, price + event venue contact
Request and map root-level seat/class/platform and a total price/currency into reservation metadata (shown on the card; price reuses the existing label). Read both the root and reservationFor and tolerate common field-name aliases (priceAmount, priceCurrencyISO4217Code, fareClass, ...) since models name these inconsistently. Also capture event/attraction venue telephone + url onto the auto-created place, matching lodging/restaurant.
This commit is contained in:
@@ -321,7 +321,7 @@ function mapEvent(r: KiReservation, source: ParsedBookingItem['source']): Parsed
|
||||
|
||||
const loc = e.location;
|
||||
const c = coords(loc?.geo);
|
||||
const venue: ParsedVenue | undefined = loc?.name ? { name: loc.name, ...(c ?? {}), address: formatAddress(loc.address) ?? undefined } : undefined;
|
||||
const venue: ParsedVenue | undefined = loc?.name ? { name: loc.name, ...(c ?? {}), address: formatAddress(loc.address) ?? undefined, website: loc.url ?? undefined, phone: loc.telephone ?? undefined } : undefined;
|
||||
|
||||
return { type: 'event', title: e.name, reservation_time: toIsoString(e.startDate ?? r.startTime), reservation_end_time: toIsoString(e.endDate ?? r.endTime), confirmation_number: r.reservationNumber ?? null, location: loc ? (formatAddress(loc.address) ?? loc.name ?? null) : null, ...(venue ? { _venue: venue } : {}), source };
|
||||
}
|
||||
@@ -330,6 +330,33 @@ function mapEvent(r: KiReservation, source: ParsedBookingItem['source']): Parsed
|
||||
// Public
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Merge seat/class/platform/price into an item's metadata (type-agnostic).
|
||||
* Models name these inconsistently and sometimes nest them under reservationFor,
|
||||
* so check both levels and common aliases. The item's own metadata wins. */
|
||||
function applyCommonMeta(item: ParsedBookingItem, r: KiReservation): ParsedBookingItem {
|
||||
const rf = (r.reservationFor && typeof r.reservationFor === 'object' ? r.reservationFor : {}) as Record<string, unknown>;
|
||||
const pick = (...keys: string[]): unknown => {
|
||||
for (const k of keys) {
|
||||
const v = (r as Record<string, unknown>)[k] ?? rf[k];
|
||||
if (v != null && v !== '') return v;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
const m: Record<string, unknown> = {};
|
||||
const seat = pick('seat', 'seatNumber');
|
||||
if (seat != null) m.seat = String(seat);
|
||||
const cls = pick('class', 'bookingClass', 'fareClass', 'serviceClass', 'seatingType');
|
||||
if (cls != null) m.class = String(cls);
|
||||
const platform = pick('platform', 'departurePlatform');
|
||||
if (platform != null) m.platform = String(platform);
|
||||
const price = pick('price', 'priceAmount', 'totalPrice', 'total');
|
||||
if (price != null) m.price = price;
|
||||
const cur = pick('priceCurrency', 'priceCurrencyISO4217Code', 'currency');
|
||||
if (cur != null) m.priceCurrency = String(cur);
|
||||
if (Object.keys(m).length) item.metadata = { ...m, ...(item.metadata ?? {}) };
|
||||
return item;
|
||||
}
|
||||
|
||||
export function mapReservations(kiItems: KiReservation[], fileName: string): { items: ParsedBookingItem[]; warnings: string[] } {
|
||||
const items: ParsedBookingItem[] = [];
|
||||
const warnings: string[] = [];
|
||||
@@ -353,7 +380,7 @@ export function mapReservations(kiItems: KiReservation[], fileName: string): { i
|
||||
group.push(kiItems[++i]);
|
||||
}
|
||||
item = group.length > 1 ? mapFlightGroup(group, source) : mapFlight(r, source);
|
||||
if (item) items.push(item);
|
||||
if (item) items.push(applyCommonMeta(item, r));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -370,7 +397,7 @@ export function mapReservations(kiItems: KiReservation[], fileName: string): { i
|
||||
warnings.push(`Unknown type "${r['@type']}" in ${fileName}[${i}] — skipped`);
|
||||
}
|
||||
|
||||
if (item) items.push(item);
|
||||
if (item) items.push(applyCommonMeta(item, r));
|
||||
}
|
||||
|
||||
return { items, warnings };
|
||||
|
||||
@@ -112,6 +112,8 @@ export interface KiEventVenue {
|
||||
name?: string;
|
||||
address?: string | KiAddress;
|
||||
geo?: KiGeo;
|
||||
telephone?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface KiEvent {
|
||||
@@ -135,6 +137,11 @@ export interface KiReservation {
|
||||
reservationFor?: Record<string, unknown>;
|
||||
pickupLocation?: KiEventVenue;
|
||||
dropoffLocation?: KiEventVenue;
|
||||
seat?: string;
|
||||
class?: string;
|
||||
platform?: string;
|
||||
price?: number | string;
|
||||
priceCurrency?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,11 @@ const ROOT_KEYS = new Set([
|
||||
'endTime',
|
||||
'pickupLocation',
|
||||
'dropoffLocation',
|
||||
'seat',
|
||||
'class',
|
||||
'platform',
|
||||
'price',
|
||||
'priceCurrency',
|
||||
'reservationFor',
|
||||
]);
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ export function buildSystemPrompt(): string {
|
||||
' LodgingReservation: { name, address, geo:{latitude,longitude}, telephone, url } — put check-in/out in root "checkinTime"/"checkoutTime"',
|
||||
' FoodEstablishmentReservation: { name, address, geo, telephone, url } — put booking time in root "startTime"/"endTime"',
|
||||
' RentalCarReservation: { name, model, make, rentalCompany:{name} } — put pickup/dropoff times in root "pickupTime"/"dropoffTime", and the pickup AND return stations in root "pickupLocation" and "dropoffLocation", each {name,address,geo:{latitude,longitude}}',
|
||||
' EventReservation / TouristAttractionVisit: { name, startDate, endDate, location:{name,address,geo} }',
|
||||
' EventReservation / TouristAttractionVisit: { name, startDate, endDate, location:{name,address,geo,telephone,url} }',
|
||||
'When present, also include at the reservation ROOT: "seat", "class" (fare/cabin class), "platform" (trains/buses), and the total "price" (a number) with "priceCurrency" (ISO 4217 code, e.g. EUR).',
|
||||
'Extract EVERY flight/segment in the document, including return legs — a round trip has TWO OR MORE flights, and each row of a flight table is a separate reservation. Do NOT stop after the first.',
|
||||
"Each flight shares the booking's reservationNumber. Use the date shown for that specific flight as its departureTime; if a flight lists only one date (no separate arrival time), leave arrivalTime null — never reuse another flight's date.",
|
||||
'If the document contains no recognizable reservation, return { "reservations": [] }.',
|
||||
|
||||
Reference in New Issue
Block a user