diff --git a/src/lib/bookingApiParameters.ts b/src/lib/bookingApiParameters.ts index c3afaa7b..554af3be 100644 --- a/src/lib/bookingApiParameters.ts +++ b/src/lib/bookingApiParameters.ts @@ -2,7 +2,7 @@ import type { BusStop } from './busStop'; import type { Capacities } from './capacities'; import type { Coordinates } from './location'; -export type CheckBookingValidityParameters = { +export type WhiteListRequest = { userChosen: Coordinates; busStops: BusStop[]; startFixed: boolean; diff --git a/src/lib/compositionTypes.ts b/src/lib/compositionTypes.ts index b2973f93..f0d985ea 100644 --- a/src/lib/compositionTypes.ts +++ b/src/lib/compositionTypes.ts @@ -2,6 +2,29 @@ import type { Capacities } from './capacities'; import type { Interval } from './interval'; import type { Coordinates } from './location'; +export type Company = { + id: number; + coordinates: Coordinates; + vehicles: Vehicle[]; + zoneId: number; + busStopFilter: boolean[]; +}; + +export type Vehicle = { + id: number; + capacities: Capacities; + tours: Tour[]; + events: Event[]; + availabilities: Interval[]; + lastEventBefore: Event | undefined; + firstEventAfter: Event | undefined; +}; + +export type Tour = { + arrival: Date; + departure: Date; +}; + export type Event = { capacities: Capacities; is_pickup: boolean; @@ -9,4 +32,9 @@ export type Event = { id: number; coordinates: Coordinates; tourId: number; + arrival: Date; + departure: Date; + communicated: Date; + approachDuration: number; + returnDuration: number; }; diff --git a/src/lib/typeAliases.ts b/src/lib/typeAliases.ts new file mode 100644 index 00000000..2c190a14 --- /dev/null +++ b/src/lib/typeAliases.ts @@ -0,0 +1 @@ +export type VehicleId = number; diff --git a/src/routes/api/blacklisting/blacklisting.test.ts b/src/routes/api/blacklisting/blacklisting.test.ts index e4c0390e..87377b96 100644 --- a/src/routes/api/blacklisting/blacklisting.test.ts +++ b/src/routes/api/blacklisting/blacklisting.test.ts @@ -1,4 +1,4 @@ -import type { CheckBookingValidityParameters } from '$lib/bookingApiParameters'; +import type { WhiteListRequest as BlackListRequest } from '$lib/bookingApiParameters'; import { Coordinates } from '$lib/location'; import { describe, it, expect, beforeEach } from 'vitest'; import { getViableBusStops } from './viableBusStops'; @@ -41,7 +41,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -58,7 +58,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -75,7 +75,7 @@ describe('blacklisting test', () => { await setTour(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -91,7 +91,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { times: [dateInXMinutes(50), dateInXMinutes(10)], coordinates: inNiesky }, @@ -110,7 +110,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -126,7 +126,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inZittau }], startFixed: true, @@ -142,7 +142,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inZittau, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -158,7 +158,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -174,7 +174,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -190,7 +190,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -206,7 +206,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -219,7 +219,7 @@ describe('blacklisting test', () => { it('blacklisting fail, no vehicle', async () => { await addCompany(Zone.NIESKY); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -230,7 +230,7 @@ describe('blacklisting test', () => { }); it('blacklisting fail, no company', async () => { - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -244,7 +244,7 @@ describe('blacklisting test', () => { const capacities: Capacities = { passengers: 3, bikes: 3, wheelchairs: 3, luggage: 3 }; const company = await addCompany(Zone.NIESKY); await addTaxi(company, capacities); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -260,7 +260,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { times: [dateInXMinutes(50)], coordinates: inZittau }, @@ -281,7 +281,7 @@ describe('blacklisting test', () => { const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [dateInXMinutes(1000), dateInXMinutes(50)], coordinates: inNiesky }], startFixed: true, @@ -294,7 +294,7 @@ describe('blacklisting test', () => { }); it('blacklisting, no busStops', async () => { - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [], startFixed: true, @@ -309,7 +309,7 @@ describe('blacklisting test', () => { const company = await addCompany(Zone.NIESKY); const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { times: [], coordinates: inNiesky }, @@ -329,7 +329,7 @@ describe('blacklisting test', () => { const company = await addCompany(Zone.NIESKY); const taxi = await addTaxi(company, capacities); await setTour(taxi, dateInXMinutes(0), dateInXMinutes(900)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [{ times: [], coordinates: inNiesky }], startFixed: true, @@ -346,7 +346,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { @@ -368,7 +368,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { @@ -390,7 +390,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { times: [dateInXMinutesYMs(0, -MAX_PASSENGER_WAITING_TIME_PICKUP)], coordinates: inNiesky } @@ -409,7 +409,7 @@ describe('blacklisting test', () => { await setAvailability(taxi, dateInXMinutes(0), dateInXMinutes(90)); - const r: CheckBookingValidityParameters = { + const r: BlackListRequest = { userChosen: inNiesky, busStops: [ { diff --git a/src/routes/api/blacklisting/viableBusStops.ts b/src/routes/api/blacklisting/viableBusStops.ts index 437cb2df..ce5ab9c6 100644 --- a/src/routes/api/blacklisting/viableBusStops.ts +++ b/src/routes/api/blacklisting/viableBusStops.ts @@ -125,7 +125,7 @@ export const getViableBusStops = async ( busStops: BusStop[], startFixed: boolean, capacities: Capacities -): Promise => { +): Promise => { if (busStops.length == 0 || !busStops.some((b) => b.times.length != 0)) { return []; } @@ -160,7 +160,7 @@ export const getViableBusStops = async ( return dbResult; }; -type BlacklistingResult = { +type BlacklistingResponse = { timeIndex: number; busstopindex: number; }; diff --git a/src/routes/api/whitelist/capacitySimulation.test.ts b/src/routes/api/whitelist/capacitySimulation.test.ts index 396e7c22..67c175db 100644 --- a/src/routes/api/whitelist/capacitySimulation.test.ts +++ b/src/routes/api/whitelist/capacitySimulation.test.ts @@ -13,7 +13,12 @@ function createEventCapacitiesOnly(capacities: Capacities, is_pickup: boolean): time: new Interval(now, now), id: 1, coordinates: new Coordinates(1, 1), - tourId: 1 + tourId: 1, + arrival: new Date(), + departure: new Date(), + communicated: new Date(), + approachDuration: 0, + returnDuration: 0 }; } diff --git a/src/routes/api/whitelist/gatherCoordinates.test.ts b/src/routes/api/whitelist/gatherCoordinates.test.ts new file mode 100644 index 00000000..b8905b91 --- /dev/null +++ b/src/routes/api/whitelist/gatherCoordinates.test.ts @@ -0,0 +1,425 @@ +import { describe, it, expect } from 'vitest'; +import { type Range } from './capacitySimulation'; +import { Coordinates } from '$lib/location'; +import type { Vehicle, Company, Event } from '$lib/compositionTypes'; +import { gatherRoutingCoordinates } from './routing'; +import { Interval } from '$lib/interval'; + +const createCompany = ( + vehicles: Vehicle[], + coordinates: Coordinates, + busStopFilter: boolean[] +): Company => { + return { + id: 1, + coordinates: coordinates, + vehicles, + zoneId: 1, + busStopFilter + }; +}; + +const createVehicle = (id: number, events: Event[]): Vehicle => { + return { + id, + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + events, + tours: [], + availabilities: [], + lastEventBefore: undefined, + firstEventAfter: undefined + }; +}; + +const createEvent = (coordinates: Coordinates): Event => { + return { + capacities: { passengers: 0, bikes: 0, wheelchairs: 0, luggage: 0 }, + is_pickup: true, + id: 1, + coordinates, + tourId: 1, + arrival: new Date(), + departure: new Date(), + communicated: new Date(), + approachDuration: 0, + returnDuration: 0, + time: new Interval(new Date(), new Date()) + }; +}; + +describe('gather coordinates test', () => { + it('insertion only possible before first event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 0 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(1); + expect(coordinates.userChosenForwardMany).toHaveLength(2); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(1); + expect(coordinates.busStopForwardMany[i]).toHaveLength(2); + } + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenBackwardMany[1]).toBe(undefined); + expect(coordinates.userChosenForwardMany[1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][1]).toBe(undefined); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][1]).toBe(undefined); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(100); + }); + it('insertion only possible after last event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 2, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(1); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(2); + expect(coordinates.busStopForwardMany[i]).toHaveLength(1); + } + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1]).toBe(undefined); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(101); + expect(coordinates.busStopForwardMany[0][1]).toBe(undefined); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][1]).toBe(undefined); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(101); + }); + it('insertion only possible between events', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 1, latestDropoff: 1 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(2); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(2); + expect(coordinates.busStopForwardMany[i]).toHaveLength(2); + } + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1].lat).toBe(101); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(100); + }); + it('insertion not possible after last event', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 1 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(3); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(2); + expect(coordinates.busStopForwardMany[i]).toHaveLength(3); + } + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenForwardMany[1].lat).toBe(100); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(100); + + expect(coordinates.userChosenForwardMany[2].lat).toBe(101); + expect(coordinates.userChosenBackwardMany[2]).toBe(undefined); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][2]).toBe(undefined); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][2]).toBe(undefined); + }); + it('2companies, all insertions possible', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ), + createCompany( + [ + createVehicle(2, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 2 }]); + insertions.set(2, [{ earliestPickup: 0, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(6); + expect(coordinates.userChosenForwardMany).toHaveLength(6); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(6); + expect(coordinates.busStopForwardMany[i]).toHaveLength(6); + } + // Company coordinates + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(6); + expect(coordinates.userChosenForwardMany[1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(6); + + // Event coordinates + expect(coordinates.userChosenBackwardMany[2].lat).toBe(100); + expect(coordinates.userChosenForwardMany[2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(100); + + expect(coordinates.userChosenBackwardMany[3].lat).toBe(101); + expect(coordinates.userChosenForwardMany[3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[0][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[0][3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][3].lat).toBe(101); + + expect(coordinates.userChosenBackwardMany[4].lat).toBe(102); + expect(coordinates.userChosenForwardMany[4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[0][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[0][4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[1][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][4].lat).toBe(102); + + expect(coordinates.userChosenBackwardMany[5].lat).toBe(103); + expect(coordinates.userChosenForwardMany[5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[0][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[0][5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[1][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[1][5].lat).toBe(103); + }); + it('2companies, 1 company-busstop combination filtered out', async () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [false, true] + ), + createCompany( + [ + createVehicle(2, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 2 }]); + insertions.set(2, [{ earliestPickup: 0, latestDropoff: 2 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(6); + expect(coordinates.userChosenForwardMany).toHaveLength(6); + expect(coordinates.busStopBackwardMany[0]).toHaveLength(3); + expect(coordinates.busStopForwardMany[0]).toHaveLength(3); + expect(coordinates.busStopBackwardMany[1]).toHaveLength(6); + expect(coordinates.busStopForwardMany[1]).toHaveLength(6); + // Company coordinates + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(6); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(6); + expect(coordinates.userChosenBackwardMany[1].lat).toBe(6); + expect(coordinates.userChosenForwardMany[1].lat).toBe(6); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(6); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(6); + // Event coordinates + expect(coordinates.userChosenBackwardMany[2].lat).toBe(100); + expect(coordinates.userChosenForwardMany[2].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][2].lat).toBe(100); + expect(coordinates.busStopForwardMany[1][2].lat).toBe(100); + + expect(coordinates.userChosenBackwardMany[3].lat).toBe(101); + expect(coordinates.userChosenForwardMany[3].lat).toBe(101); + expect(coordinates.busStopBackwardMany[1][3].lat).toBe(101); + expect(coordinates.busStopForwardMany[1][3].lat).toBe(101); + + expect(coordinates.userChosenBackwardMany[4].lat).toBe(102); + expect(coordinates.userChosenForwardMany[4].lat).toBe(102); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(102); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(102); + expect(coordinates.busStopBackwardMany[1][4].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][4].lat).toBe(102); + + expect(coordinates.userChosenBackwardMany[5].lat).toBe(103); + expect(coordinates.userChosenForwardMany[5].lat).toBe(103); + expect(coordinates.busStopBackwardMany[0][2].lat).toBe(103); + expect(coordinates.busStopForwardMany[0][2].lat).toBe(103); + expect(coordinates.busStopBackwardMany[1][5].lat).toBe(103); + expect(coordinates.busStopForwardMany[1][5].lat).toBe(103); + }); + it('insertion only possible before first event, lastEventBefore exists', () => { + let eventLatLng = 100; + let companyLatLng = 5; + const companies = [ + createCompany( + [ + createVehicle(1, [ + createEvent(new Coordinates(eventLatLng, eventLatLng++)), + createEvent(new Coordinates(eventLatLng, eventLatLng++)) + ]) + ], + new Coordinates(companyLatLng, companyLatLng++), + [true, true] + ) + ]; + companies[0].vehicles[0].lastEventBefore = createEvent( + new Coordinates(eventLatLng, eventLatLng++) + ); + const insertions = new Map(); + insertions.set(1, [{ earliestPickup: 0, latestDropoff: 0 }]); + const coordinates = gatherRoutingCoordinates(companies, insertions); + expect(coordinates.busStopBackwardMany).toHaveLength(2); + expect(coordinates.busStopForwardMany).toHaveLength(2); + expect(coordinates.userChosenBackwardMany).toHaveLength(2); + expect(coordinates.userChosenForwardMany).toHaveLength(2); + for (let i = 0; i != 2; ++i) { + expect(coordinates.busStopBackwardMany[i]).toHaveLength(2); + expect(coordinates.busStopForwardMany[i]).toHaveLength(2); + } + // company + expect(coordinates.userChosenBackwardMany[0].lat).toBe(5); + expect(coordinates.userChosenForwardMany[0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[0][0].lat).toBe(5); + expect(coordinates.busStopBackwardMany[1][0].lat).toBe(5); + expect(coordinates.busStopForwardMany[1][0].lat).toBe(5); + + // event + expect(coordinates.userChosenBackwardMany[1].lat).toBe(102); + expect(coordinates.userChosenForwardMany[1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[0][1].lat).toBe(102); + expect(coordinates.busStopForwardMany[0][1].lat).toBe(100); + expect(coordinates.busStopBackwardMany[1][1].lat).toBe(102); + expect(coordinates.busStopForwardMany[1][1].lat).toBe(100); + }); +}); diff --git a/src/routes/api/whitelist/insertionTypes.ts b/src/routes/api/whitelist/insertionTypes.ts new file mode 100644 index 00000000..01cff501 --- /dev/null +++ b/src/routes/api/whitelist/insertionTypes.ts @@ -0,0 +1,11 @@ +import type { Vehicle } from '$lib/compositionTypes'; +import type { Range } from './capacitySimulation'; + +export type InsertionInfo = { + companyIdx: number; + prevEventIdxInRoutingResults: number; + nextEventIdxInRoutingResults: number; + vehicle: Vehicle; + insertionIdx: number; + currentRange: Range; +}; diff --git a/src/routes/api/whitelist/routing.ts b/src/routes/api/whitelist/routing.ts new file mode 100644 index 00000000..4dca2286 --- /dev/null +++ b/src/routes/api/whitelist/routing.ts @@ -0,0 +1,143 @@ +import { oneToMany } from '$lib/api'; +import type { BusStop } from '$lib/busStop'; +import type { Company } from '$lib/compositionTypes'; +import { Coordinates } from '$lib/location'; +import type { VehicleId } from '$lib/typeAliases'; +import type { Range } from './capacitySimulation'; +import { iterateAllInsertions } from './utils'; + +export type InsertionRoutingResult = { + fromCompany: number[]; + toCompany: number[]; + fromPrevEvent: number[]; + toNextEvent: number[]; +}; + +export type RoutingResults = { + busStops: InsertionRoutingResult[]; + userChosen: InsertionRoutingResult; +}; + +type RoutingCoordinates = { + busStopForwardMany: Coordinates[][]; + busStopBackwardMany: Coordinates[][]; + userChosenForwardMany: Coordinates[]; + userChosenBackwardMany: Coordinates[]; +}; + +export function gatherRoutingCoordinates( + companies: Company[], + insertionsByVehicle: Map +): RoutingCoordinates { + if (companies.length == 0 || companies[0].busStopFilter.length == 0) { + return { + busStopBackwardMany: [], + busStopForwardMany: [], + userChosenBackwardMany: [], + userChosenForwardMany: [] + }; + } + const busStopCount = companies[0].busStopFilter.length; + const userChosenForwardMany = new Array(); + const userChosenBackwardMany = new Array(); + const busStopForwardMany = new Array(busStopCount); + const busStopBackwardMany = new Array(busStopCount); + for (let busStopIdx = 0; busStopIdx != busStopCount; ++busStopIdx) { + busStopForwardMany[busStopIdx] = new Array(); + busStopBackwardMany[busStopIdx] = new Array(); + } + companies.forEach((company) => { + for (let busStopIdx = 0; busStopIdx != busStopCount; ++busStopIdx) { + if (!company.busStopFilter[busStopIdx]) { + continue; + } + busStopForwardMany[busStopIdx].push(company.coordinates); + busStopBackwardMany[busStopIdx].push(company.coordinates); + } + userChosenForwardMany.push(company.coordinates); + userChosenBackwardMany.push(company.coordinates); + }); + iterateAllInsertions( + companies, + insertionsByVehicle, + (insertionInfo, _insertionCounter, busStopFilter) => { + const backwardCoordinates = ( + insertionInfo.insertionIdx != 0 + ? insertionInfo.vehicle.events[insertionInfo.insertionIdx - 1] + : insertionInfo.vehicle.lastEventBefore + )?.coordinates; + const forwardCoordinates = ( + insertionInfo.insertionIdx != insertionInfo.vehicle.events.length + ? insertionInfo.vehicle.events[insertionInfo.insertionIdx] + : insertionInfo.vehicle.firstEventAfter + )?.coordinates; + if (backwardCoordinates != undefined) { + userChosenBackwardMany.push(backwardCoordinates); + } + if (forwardCoordinates != undefined) { + userChosenForwardMany.push(forwardCoordinates); + } + for (let busStopIdx = 0; busStopIdx != busStopFilter.length; ++busStopIdx) { + if (!busStopFilter[busStopIdx]) { + continue; + } + if (backwardCoordinates != undefined) { + busStopBackwardMany[busStopIdx].push(backwardCoordinates); + } + if (forwardCoordinates != undefined) { + busStopForwardMany[busStopIdx].push(forwardCoordinates); + } + } + } + ); + return { + busStopForwardMany, + busStopBackwardMany, + userChosenForwardMany, + userChosenBackwardMany + }; +} + +export async function routing( + coordinates: RoutingCoordinates, + userChosen: Coordinates, + companies: Company[], + busStops: BusStop[] +): Promise { + // true = many to one + // false = one to many + const from = await oneToMany(userChosen, coordinates.userChosenBackwardMany, true); + const to = await oneToMany(userChosen, coordinates.userChosenForwardMany, false); + const routingResults = { + userChosen: { + fromCompany: from.slice(0, companies.length), + fromPrevEvent: from.slice(companies.length), + toCompany: to.slice(0, companies.length), + toNextEvent: to.slice(companies.length) + }, + busStops: new Array(busStops.length) + }; + for (let busStopIdx = 0; busStopIdx != busStops.length; ++busStopIdx) { + const busStop = busStops[busStopIdx]; + const relevantCompanyCount = companies.filter( + (company) => company.busStopFilter[busStopIdx] + ).length; + const from = await oneToMany( + busStop.coordinates, + coordinates.busStopBackwardMany[busStopIdx], + true + ); + const to = await oneToMany( + busStop.coordinates, + coordinates.busStopForwardMany[busStopIdx], + false + ); + routingResults.busStops[busStopIdx] = { + fromCompany: from.slice(0, relevantCompanyCount), + fromPrevEvent: from.slice(relevantCompanyCount), + toCompany: to.slice(0, relevantCompanyCount), + toNextEvent: to.slice(relevantCompanyCount) + }; + } + return routingResults; +} diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts new file mode 100644 index 00000000..ad4fcdb5 --- /dev/null +++ b/src/routes/api/whitelist/utils.ts @@ -0,0 +1,42 @@ +import type { Company } from '$lib/compositionTypes'; +import type { VehicleId } from '$lib/typeAliases'; +import type { Range } from './capacitySimulation'; +import type { InsertionInfo } from './insertionTypes'; + +export function iterateAllInsertions( + companies: Company[], + insertions: Map, + insertionFn: (info: InsertionInfo, insertionCounter: number, busStopFilter: boolean[]) => void +) { + let prevEventIdxInRoutingResults = 0; + let nextEventIdxInRoutingResults = 0; + let insertionIdx = 0; + companies.forEach((company, companyIdx) => { + company.vehicles.forEach((vehicle) => { + insertions.get(vehicle.id)!.forEach((insertion) => { + for ( + let idxInEvents = insertion.earliestPickup; + idxInEvents != insertion.latestDropoff + 1; + ++idxInEvents + ) { + const info = { + insertionIdx: idxInEvents, + companyIdx, + vehicle, + prevEventIdxInRoutingResults, + nextEventIdxInRoutingResults, + currentRange: insertion + }; + insertionFn(info, insertionIdx, company.busStopFilter); + if (idxInEvents != 0 || vehicle.lastEventBefore != undefined) { + prevEventIdxInRoutingResults++; + } + if (idxInEvents != vehicle.events.length || vehicle.firstEventAfter != undefined) { + nextEventIdxInRoutingResults++; + } + insertionIdx++; + } + }); + }); + }); +} diff --git a/tests/db.setup.ts b/tests/db.setup.ts index 12911174..500d98ba 100644 --- a/tests/db.setup.ts +++ b/tests/db.setup.ts @@ -31,9 +31,9 @@ setup('setup db', async () => { await migrator.migrateToLatest(); await db.deleteFrom('user_session').executeTakeFirstOrThrow(); - await db.deleteFrom('auth_user').executeTakeFirstOrThrow(); await db.deleteFrom('availability').executeTakeFirstOrThrow(); await db.deleteFrom('event').executeTakeFirstOrThrow(); + await db.deleteFrom('auth_user').executeTakeFirstOrThrow(); await db.deleteFrom('request').executeTakeFirstOrThrow(); await db.deleteFrom('tour').executeTakeFirstOrThrow(); await db.deleteFrom('vehicle').executeTakeFirstOrThrow();