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/insertionTypes.ts b/src/routes/api/whitelist/insertionTypes.ts new file mode 100644 index 00000000..2ade42a5 --- /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; + idxInEvents: 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..ec6b7c6e --- /dev/null +++ b/src/routes/api/whitelist/routing.ts @@ -0,0 +1,80 @@ +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 = { + company: number[]; + event: number[]; +}; + +export type RoutingResults = { + busStops: InsertionRoutingResult[]; + userChosen: InsertionRoutingResult; +}; + +export function gatherRoutingCoordinates( + companies: Company[], + insertionsByVehicle: Map +) { + if (companies.length == 0) { + return { forward: [], backward: [] }; + } + const backward = new Array(); + const forward = new Array(); + companies.forEach((company) => { + forward.push(company.coordinates); + backward.push(company.coordinates); + }); + iterateAllInsertions(companies, insertionsByVehicle, (insertionInfo, _insertionCounter) => { + const vehicle = insertionInfo.vehicle; + const idxInEvents = insertionInfo.idxInEvents; + if (idxInEvents != 0) { + backward.push(vehicle.events[idxInEvents - 1].coordinates); + } else if (vehicle.lastEventBefore != undefined) { + backward.push(vehicle.lastEventBefore.coordinates); + } + if (idxInEvents != vehicle.events.length) { + forward.push(vehicle.events[idxInEvents].coordinates); + } else if (vehicle.firstEventAfter != undefined) { + forward.push(vehicle.firstEventAfter.coordinates); + } + }); + return { forward, backward }; +} + +export async function routing( + companies: Company[], + many: { forward: Coordinates[]; backward: Coordinates[] }, + userChosen: Coordinates, + busStops: BusStop[], + startFixed: boolean +): Promise { + const userChosenResult = await oneToMany( + userChosen, + startFixed ? many.backward : many.forward, + !startFixed + ); + const ret = { + userChosen: { + company: userChosenResult.slice(0, companies.length), + event: userChosenResult.slice(companies.length) + }, + busStops: new Array(busStops.length) + }; + for (let busStopIdx = 0; busStopIdx != busStops.length; ++busStopIdx) { + const busStopResult = await oneToMany( + busStops[busStopIdx].coordinates, + !startFixed ? many.backward : many.forward, + startFixed + ); + ret.busStops[busStopIdx] = { + company: busStopResult.slice(0, companies.length), + event: busStopResult.slice(companies.length) + }; + } + return ret; +} diff --git a/src/routes/api/whitelist/utils.ts b/src/routes/api/whitelist/utils.ts new file mode 100644 index 00000000..27aa368f --- /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 = { + 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();