compute journeys with tests
This commit is contained in:
parent
467d8a84f8
commit
d8df086c6d
|
@ -20,7 +20,7 @@ export abstract class Algorithm {
|
||||||
for (const processor of this.processors) {
|
for (const processor of this.processors) {
|
||||||
this.candidates = await processor.execute(this.candidates);
|
this.candidates = await processor.execute(this.candidates);
|
||||||
}
|
}
|
||||||
console.log(JSON.stringify(this.candidates, null, 2));
|
// console.log(JSON.stringify(this.candidates, null, 2));
|
||||||
return this.candidates.map((candidate: CandidateEntity) =>
|
return this.candidates.map((candidate: CandidateEntity) =>
|
||||||
MatchEntity.create({ adId: candidate.id }),
|
MatchEntity.create({ adId: candidate.id }),
|
||||||
);
|
);
|
||||||
|
|
|
@ -63,14 +63,16 @@ export class CalendarTools {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a date from a date and time as strings, adding optional seconds
|
* Returns a date from a date (as a date) and a time (as a string), adding optional seconds
|
||||||
*/
|
*/
|
||||||
static datetimeFromString = (
|
static datetimeWithSeconds = (
|
||||||
date: string,
|
date: Date,
|
||||||
time: string,
|
time: string,
|
||||||
additionalSeconds = 0,
|
additionalSeconds = 0,
|
||||||
): Date => {
|
): Date => {
|
||||||
const datetime = new Date(`${date}T${time}:00Z`);
|
const datetime: Date = new Date(date);
|
||||||
|
datetime.setUTCHours(parseInt(time.split(':')[0]));
|
||||||
|
datetime.setUTCMinutes(parseInt(time.split(':')[1]));
|
||||||
datetime.setUTCSeconds(additionalSeconds);
|
datetime.setUTCSeconds(additionalSeconds);
|
||||||
return datetime;
|
return datetime;
|
||||||
};
|
};
|
||||||
|
@ -79,7 +81,7 @@ export class CalendarTools {
|
||||||
* Returns dates from a day and time based on unix epoch day
|
* Returns dates from a day and time based on unix epoch day
|
||||||
* (1970-01-01 is day 4)
|
* (1970-01-01 is day 4)
|
||||||
* The method returns an array of dates because for edges (day 0 and 6)
|
* The method returns an array of dates because for edges (day 0 and 6)
|
||||||
* we need to return 2 possibilities
|
* we need to return 2 possibilities : one for the previous week, one for the next week
|
||||||
*/
|
*/
|
||||||
static epochDaysFromTime = (weekDay: number, time: string): Date[] => {
|
static epochDaysFromTime = (weekDay: number, time: string): Date[] => {
|
||||||
if (weekDay < 0 || weekDay > 6)
|
if (weekDay < 0 || weekDay > 6)
|
||||||
|
|
|
@ -46,6 +46,7 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the journeys based on the driver schedule (the driver 'drives' the carpool !)
|
* Create the journeys based on the driver schedule (the driver 'drives' the carpool !)
|
||||||
|
* This is a tedious process : additional information can be found in deeper methods !
|
||||||
*/
|
*/
|
||||||
createJourneys = (): CandidateEntity => {
|
createJourneys = (): CandidateEntity => {
|
||||||
this.props.journeys = this.props.driverSchedule.map(
|
this.props.journeys = this.props.driverSchedule.map(
|
||||||
|
@ -90,6 +91,13 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
this._createJourneyItem(carpoolPathItem, index, driverScheduleItem),
|
this._createJourneyItem(carpoolPathItem, index, driverScheduleItem),
|
||||||
) as JourneyItem[];
|
) as JourneyItem[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a journey item based on a carpool path item and driver schedule item
|
||||||
|
* The stepIndex is used to get the duration to reach the carpool path item
|
||||||
|
* from the steps prop (computed previously by a georouter)
|
||||||
|
* There MUST be a one/one relation between the carpool path items indexes
|
||||||
|
* and the steps indexes.
|
||||||
|
*/
|
||||||
private _createJourneyItem = (
|
private _createJourneyItem = (
|
||||||
carpoolPathItem: CarpoolPathItem,
|
carpoolPathItem: CarpoolPathItem,
|
||||||
stepIndex: number,
|
stepIndex: number,
|
||||||
|
@ -123,42 +131,55 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
actor.target == Target.START
|
actor.target == Target.START
|
||||||
? 0
|
? 0
|
||||||
: duration;
|
: duration;
|
||||||
|
const firstDate: Date = CalendarTools.firstDate(
|
||||||
|
scheduleItem.day,
|
||||||
|
this.props.dateInterval,
|
||||||
|
);
|
||||||
|
const lastDate: Date = CalendarTools.lastDate(
|
||||||
|
scheduleItem.day,
|
||||||
|
this.props.dateInterval,
|
||||||
|
);
|
||||||
return new ActorTime({
|
return new ActorTime({
|
||||||
role: actor.role,
|
role: actor.role,
|
||||||
target: actor.target,
|
target: actor.target,
|
||||||
firstDatetime: CalendarTools.datetimeFromString(
|
firstDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.lowerDate,
|
firstDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
effectiveDuration,
|
effectiveDuration,
|
||||||
),
|
),
|
||||||
firstMinDatetime: CalendarTools.datetimeFromString(
|
firstMinDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.lowerDate,
|
firstDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
-scheduleItem.margin + effectiveDuration,
|
-scheduleItem.margin + effectiveDuration,
|
||||||
),
|
),
|
||||||
firstMaxDatetime: CalendarTools.datetimeFromString(
|
firstMaxDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.lowerDate,
|
firstDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
scheduleItem.margin + effectiveDuration,
|
scheduleItem.margin + effectiveDuration,
|
||||||
),
|
),
|
||||||
lastDatetime: CalendarTools.datetimeFromString(
|
lastDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.higherDate,
|
lastDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
effectiveDuration,
|
effectiveDuration,
|
||||||
),
|
),
|
||||||
lastMinDatetime: CalendarTools.datetimeFromString(
|
lastMinDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.higherDate,
|
lastDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
-scheduleItem.margin + effectiveDuration,
|
-scheduleItem.margin + effectiveDuration,
|
||||||
),
|
),
|
||||||
lastMaxDatetime: CalendarTools.datetimeFromString(
|
lastMaxDatetime: CalendarTools.datetimeWithSeconds(
|
||||||
this.props.dateInterval.higherDate,
|
lastDate,
|
||||||
scheduleItem.time,
|
scheduleItem.time,
|
||||||
scheduleItem.margin + effectiveDuration,
|
scheduleItem.margin + effectiveDuration,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the closest (in time) passenger schedule item for a given driver schedule item
|
||||||
|
* This is mandatory as we can't rely only on the day of the schedule item :
|
||||||
|
* items on different days can match when playing with margins around midnight
|
||||||
|
*/
|
||||||
private _closestPassengerScheduleItem = (
|
private _closestPassengerScheduleItem = (
|
||||||
driverScheduleItem: ScheduleItem,
|
driverScheduleItem: ScheduleItem,
|
||||||
): ScheduleItem =>
|
): ScheduleItem =>
|
||||||
|
@ -179,8 +200,12 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
: currentScheduleItemGap,
|
: currentScheduleItemGap,
|
||||||
).scheduleItem;
|
).scheduleItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the passenger schedule item with the minimum duration between a given date and the dates of the passenger schedule
|
||||||
|
*/
|
||||||
private _minPassengerScheduleItemGapForDate = (date: Date): ScheduleItemGap =>
|
private _minPassengerScheduleItemGapForDate = (date: Date): ScheduleItemGap =>
|
||||||
this.props.passengerSchedule
|
this.props.passengerSchedule
|
||||||
|
// first map the passenger schedule to "real" dates (we use unix epoch date as base)
|
||||||
.map(
|
.map(
|
||||||
(scheduleItem: ScheduleItem) =>
|
(scheduleItem: ScheduleItem) =>
|
||||||
<ScheduleItemRange>{
|
<ScheduleItemRange>{
|
||||||
|
@ -191,16 +216,21 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
// then compute the duration in seconds to the given date
|
||||||
|
// for each "real" date computed in step 1
|
||||||
.map((scheduleItemRange: ScheduleItemRange) => ({
|
.map((scheduleItemRange: ScheduleItemRange) => ({
|
||||||
scheduleItem: scheduleItemRange.scheduleItem,
|
scheduleItem: scheduleItemRange.scheduleItem,
|
||||||
gap: scheduleItemRange.range
|
gap: scheduleItemRange.range
|
||||||
|
// compute the duration
|
||||||
.map((scheduleDate: Date) =>
|
.map((scheduleDate: Date) =>
|
||||||
Math.round(Math.abs(scheduleDate.getTime() - date.getTime())),
|
Math.round(Math.abs(scheduleDate.getTime() - date.getTime())),
|
||||||
)
|
)
|
||||||
|
// keep the lowest duration
|
||||||
.reduce((previousGap: number, currentGap: number) =>
|
.reduce((previousGap: number, currentGap: number) =>
|
||||||
previousGap < currentGap ? previousGap : currentGap,
|
previousGap < currentGap ? previousGap : currentGap,
|
||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
|
// finally, keep the passenger schedule item with the lowest duration
|
||||||
.reduce(
|
.reduce(
|
||||||
(
|
(
|
||||||
previousScheduleItemGap: ScheduleItemGap,
|
previousScheduleItemGap: ScheduleItemGap,
|
||||||
|
|
|
@ -90,27 +90,26 @@ describe('Calendar tools service', () => {
|
||||||
|
|
||||||
describe('Datetime from string', () => {
|
describe('Datetime from string', () => {
|
||||||
it('should return a date with time from a string without additional seconds', () => {
|
it('should return a date with time from a string without additional seconds', () => {
|
||||||
const datetime: Date = CalendarTools.datetimeFromString(
|
const datetime: Date = CalendarTools.datetimeWithSeconds(
|
||||||
'2023-09-01',
|
new Date('2023-09-01'),
|
||||||
'07:12',
|
'07:12',
|
||||||
);
|
);
|
||||||
expect(datetime.getUTCMinutes()).toBe(12);
|
expect(datetime.getUTCMinutes()).toBe(12);
|
||||||
});
|
});
|
||||||
it('should return a date with time from a string with additional seconds', () => {
|
it('should return a date with time from a string with additional seconds', () => {
|
||||||
const datetime: Date = CalendarTools.datetimeFromString(
|
const datetime: Date = CalendarTools.datetimeWithSeconds(
|
||||||
'2023-09-01',
|
new Date('2023-09-01'),
|
||||||
'07:12',
|
'07:12',
|
||||||
60,
|
60,
|
||||||
);
|
);
|
||||||
expect(datetime.getUTCMinutes()).toBe(13);
|
expect(datetime.getUTCMinutes()).toBe(13);
|
||||||
});
|
});
|
||||||
it('should return a date with time from a string with negative additional seconds', () => {
|
it('should return a date with time from a string with negative additional seconds', () => {
|
||||||
const datetime: Date = CalendarTools.datetimeFromString(
|
const datetime: Date = CalendarTools.datetimeWithSeconds(
|
||||||
'2023-09-01',
|
new Date('2023-09-01'),
|
||||||
'07:00',
|
'07:00',
|
||||||
-60,
|
-60,
|
||||||
);
|
);
|
||||||
console.log(datetime);
|
|
||||||
expect(datetime.getUTCHours()).toBe(6);
|
expect(datetime.getUTCHours()).toBe(6);
|
||||||
expect(datetime.getUTCMinutes()).toBe(59);
|
expect(datetime.getUTCMinutes()).toBe(59);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,244 @@
|
||||||
import { Role } from '@modules/ad/core/domain/ad.types';
|
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||||
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
import { Target } from '@modules/ad/core/domain/candidate.types';
|
import {
|
||||||
|
SpacetimeDetourRatio,
|
||||||
|
Target,
|
||||||
|
} from '@modules/ad/core/domain/candidate.types';
|
||||||
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||||
|
import { CarpoolPathItemProps } from '@modules/ad/core/domain/value-objects/carpool-path-item.value-object';
|
||||||
|
import { Journey } from '@modules/ad/core/domain/value-objects/journey.value-object';
|
||||||
|
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
|
import { ScheduleItemProps } from '@modules/ad/core/domain/value-objects/schedule-item.value-object';
|
||||||
|
import { StepProps } from '@modules/ad/core/domain/value-objects/step.value-object';
|
||||||
|
|
||||||
|
const waypointsSet1: PointProps[] = [
|
||||||
|
{
|
||||||
|
lat: 48.678454,
|
||||||
|
lon: 6.189745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.84877,
|
||||||
|
lon: 2.398457,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const waypointsSet2: PointProps[] = [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule1: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '07:00',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule2: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '07:10',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule3: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '06:30',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 2,
|
||||||
|
time: '06:30',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 3,
|
||||||
|
time: '06:00',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 4,
|
||||||
|
time: '06:30',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 5,
|
||||||
|
time: '06:30',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule4: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '06:50',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 2,
|
||||||
|
time: '06:50',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 4,
|
||||||
|
time: '06:50',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 5,
|
||||||
|
time: '06:50',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule5: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 0,
|
||||||
|
time: '00:10',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '07:05',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const schedule6: ScheduleItemProps[] = [
|
||||||
|
{
|
||||||
|
day: 1,
|
||||||
|
time: '23:10',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: 6,
|
||||||
|
time: '23:45',
|
||||||
|
margin: 900,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const spacetimeDetourRatio: SpacetimeDetourRatio = {
|
||||||
|
maxDistanceDetourRatio: 0.3,
|
||||||
|
maxDurationDetourRatio: 0.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const carpoolPath1: CarpoolPathItemProps[] = [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const carpoolPath2: CarpoolPathItemProps[] = [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.678451,
|
||||||
|
lon: 6.168784,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.NEUTRAL,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.848715,
|
||||||
|
lon: 2.36985,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.NEUTRAL,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const steps: StepProps[] = [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
duration: 0,
|
||||||
|
distance: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.678451,
|
||||||
|
lon: 6.168784,
|
||||||
|
duration: 1254,
|
||||||
|
distance: 33462,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.848715,
|
||||||
|
lon: 2.36985,
|
||||||
|
duration: 12477,
|
||||||
|
distance: 343654,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
duration: 13548,
|
||||||
|
distance: 350145,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
describe('Candidate entity', () => {
|
describe('Candidate entity', () => {
|
||||||
it('should create a new candidate entity', () => {
|
it('should create a new candidate entity', () => {
|
||||||
|
@ -12,46 +249,13 @@ describe('Candidate entity', () => {
|
||||||
lowerDate: '2023-08-28',
|
lowerDate: '2023-08-28',
|
||||||
higherDate: '2023-08-28',
|
higherDate: '2023-08-28',
|
||||||
},
|
},
|
||||||
driverWaypoints: [
|
driverWaypoints: waypointsSet1,
|
||||||
{
|
passengerWaypoints: waypointsSet2,
|
||||||
lat: 48.678454,
|
|
||||||
lon: 6.189745,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.84877,
|
|
||||||
lon: 2.398457,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerWaypoints: [
|
|
||||||
{
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: [
|
driverSchedule: schedule1,
|
||||||
{
|
passengerSchedule: schedule2,
|
||||||
day: 0,
|
spacetimeDetourRatio,
|
||||||
time: '07:00',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerSchedule: [
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
time: '07:10',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spacetimeDetourRatio: {
|
|
||||||
maxDistanceDetourRatio: 0.3,
|
|
||||||
maxDurationDetourRatio: 0.3,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
expect(candidateEntity.id.length).toBe(36);
|
expect(candidateEntity.id.length).toBe(36);
|
||||||
});
|
});
|
||||||
|
@ -64,76 +268,14 @@ describe('Candidate entity', () => {
|
||||||
lowerDate: '2023-08-28',
|
lowerDate: '2023-08-28',
|
||||||
higherDate: '2023-08-28',
|
higherDate: '2023-08-28',
|
||||||
},
|
},
|
||||||
driverWaypoints: [
|
driverWaypoints: waypointsSet2,
|
||||||
{
|
passengerWaypoints: waypointsSet2,
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerWaypoints: [
|
|
||||||
{
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: [
|
driverSchedule: schedule1,
|
||||||
{
|
passengerSchedule: schedule2,
|
||||||
day: 0,
|
spacetimeDetourRatio,
|
||||||
time: '07:00',
|
}).setCarpoolPath(carpoolPath1);
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerSchedule: [
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
time: '07:10',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spacetimeDetourRatio: {
|
|
||||||
maxDistanceDetourRatio: 0.3,
|
|
||||||
maxDurationDetourRatio: 0.3,
|
|
||||||
},
|
|
||||||
}).setCarpoolPath([
|
|
||||||
{
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
actors: [
|
|
||||||
new Actor({
|
|
||||||
role: Role.DRIVER,
|
|
||||||
target: Target.START,
|
|
||||||
}),
|
|
||||||
new Actor({
|
|
||||||
role: Role.PASSENGER,
|
|
||||||
target: Target.START,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
actors: [
|
|
||||||
new Actor({
|
|
||||||
role: Role.DRIVER,
|
|
||||||
target: Target.FINISH,
|
|
||||||
}),
|
|
||||||
new Actor({
|
|
||||||
role: Role.PASSENGER,
|
|
||||||
target: Target.FINISH,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
expect(candidateEntity.getProps().carpoolPath).toHaveLength(2);
|
expect(candidateEntity.getProps().carpoolPath).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -145,46 +287,13 @@ describe('Candidate entity', () => {
|
||||||
lowerDate: '2023-08-28',
|
lowerDate: '2023-08-28',
|
||||||
higherDate: '2023-08-28',
|
higherDate: '2023-08-28',
|
||||||
},
|
},
|
||||||
driverWaypoints: [
|
driverWaypoints: waypointsSet1,
|
||||||
{
|
passengerWaypoints: waypointsSet2,
|
||||||
lat: 48.678454,
|
|
||||||
lon: 6.189745,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.84877,
|
|
||||||
lon: 2.398457,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerWaypoints: [
|
|
||||||
{
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: [
|
driverSchedule: schedule1,
|
||||||
{
|
passengerSchedule: schedule2,
|
||||||
day: 0,
|
spacetimeDetourRatio,
|
||||||
time: '07:00',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerSchedule: [
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
time: '07:10',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spacetimeDetourRatio: {
|
|
||||||
maxDistanceDetourRatio: 0.3,
|
|
||||||
maxDurationDetourRatio: 0.3,
|
|
||||||
},
|
|
||||||
}).setMetrics(352688, 14587);
|
}).setMetrics(352688, 14587);
|
||||||
expect(candidateEntity.getProps().distance).toBe(352688);
|
expect(candidateEntity.getProps().distance).toBe(352688);
|
||||||
expect(candidateEntity.getProps().duration).toBe(14587);
|
expect(candidateEntity.getProps().duration).toBe(14587);
|
||||||
|
@ -199,46 +308,13 @@ describe('Candidate entity', () => {
|
||||||
lowerDate: '2023-08-28',
|
lowerDate: '2023-08-28',
|
||||||
higherDate: '2023-08-28',
|
higherDate: '2023-08-28',
|
||||||
},
|
},
|
||||||
driverWaypoints: [
|
driverWaypoints: waypointsSet1,
|
||||||
{
|
passengerWaypoints: waypointsSet2,
|
||||||
lat: 48.678454,
|
|
||||||
lon: 6.189745,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.84877,
|
|
||||||
lon: 2.398457,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerWaypoints: [
|
|
||||||
{
|
|
||||||
lat: 48.849445,
|
|
||||||
lon: 6.68651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 47.18746,
|
|
||||||
lon: 2.89742,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: [
|
driverSchedule: schedule1,
|
||||||
{
|
passengerSchedule: schedule2,
|
||||||
day: 0,
|
spacetimeDetourRatio,
|
||||||
time: '07:00',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerSchedule: [
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
time: '07:10',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spacetimeDetourRatio: {
|
|
||||||
maxDistanceDetourRatio: 0.3,
|
|
||||||
maxDurationDetourRatio: 0.3,
|
|
||||||
},
|
|
||||||
}).setMetrics(458690, 13980);
|
}).setMetrics(458690, 13980);
|
||||||
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
@ -250,52 +326,120 @@ describe('Candidate entity', () => {
|
||||||
lowerDate: '2023-08-28',
|
lowerDate: '2023-08-28',
|
||||||
higherDate: '2023-08-28',
|
higherDate: '2023-08-28',
|
||||||
},
|
},
|
||||||
driverWaypoints: [
|
driverWaypoints: waypointsSet1,
|
||||||
{
|
passengerWaypoints: waypointsSet2,
|
||||||
lat: 48.678454,
|
|
||||||
lon: 6.189745,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 48.84877,
|
|
||||||
lon: 2.398457,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerWaypoints: [
|
|
||||||
{
|
|
||||||
lat: 48.849445,
|
|
||||||
lon: 6.68651,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lat: 47.18746,
|
|
||||||
lon: 2.89742,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: [
|
driverSchedule: schedule1,
|
||||||
{
|
passengerSchedule: schedule2,
|
||||||
day: 0,
|
spacetimeDetourRatio,
|
||||||
time: '07:00',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
passengerSchedule: [
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
time: '07:10',
|
|
||||||
margin: 900,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spacetimeDetourRatio: {
|
|
||||||
maxDistanceDetourRatio: 0.3,
|
|
||||||
maxDurationDetourRatio: 0.3,
|
|
||||||
},
|
|
||||||
}).setMetrics(352368, 18314);
|
}).setMetrics(352368, 18314);
|
||||||
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Journeys', () => {
|
describe('Journeys', () => {
|
||||||
it('should create journeys', () => {});
|
it('should create journeys for a single date', () => {
|
||||||
|
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
dateInterval: {
|
||||||
|
lowerDate: '2023-08-28',
|
||||||
|
higherDate: '2023-08-28',
|
||||||
|
},
|
||||||
|
driverWaypoints: waypointsSet1,
|
||||||
|
passengerWaypoints: waypointsSet2,
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
driverSchedule: schedule1,
|
||||||
|
passengerSchedule: schedule2,
|
||||||
|
spacetimeDetourRatio,
|
||||||
|
})
|
||||||
|
.setCarpoolPath(carpoolPath2)
|
||||||
|
.setSteps(steps)
|
||||||
|
.createJourneys();
|
||||||
|
expect(candidateEntity.getProps().journeys).toHaveLength(1);
|
||||||
|
});
|
||||||
|
it('should create journeys for multiple dates', () => {
|
||||||
|
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
dateInterval: {
|
||||||
|
lowerDate: '2023-09-01',
|
||||||
|
higherDate: '2024-09-01',
|
||||||
|
},
|
||||||
|
driverWaypoints: waypointsSet1,
|
||||||
|
passengerWaypoints: waypointsSet2,
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
driverSchedule: schedule3,
|
||||||
|
passengerSchedule: schedule4,
|
||||||
|
spacetimeDetourRatio,
|
||||||
|
})
|
||||||
|
.setCarpoolPath(carpoolPath2)
|
||||||
|
.setSteps(steps)
|
||||||
|
.createJourneys();
|
||||||
|
expect(candidateEntity.getProps().journeys).toHaveLength(5);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].firstDate.getDate(),
|
||||||
|
).toBe(4);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getDate(),
|
||||||
|
).toBe(4);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[1].journeyItems[1].actorTimes[1].firstMinDatetime.getDate(),
|
||||||
|
).toBe(5);
|
||||||
|
});
|
||||||
|
it('should create journeys for multiple dates, including week edges (saturday/sunday)', () => {
|
||||||
|
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
dateInterval: {
|
||||||
|
lowerDate: '2023-09-01',
|
||||||
|
higherDate: '2024-09-01',
|
||||||
|
},
|
||||||
|
driverWaypoints: waypointsSet1,
|
||||||
|
passengerWaypoints: waypointsSet2,
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
driverSchedule: schedule5,
|
||||||
|
passengerSchedule: schedule6,
|
||||||
|
spacetimeDetourRatio,
|
||||||
|
})
|
||||||
|
.setCarpoolPath(carpoolPath2)
|
||||||
|
.setSteps(steps)
|
||||||
|
.createJourneys();
|
||||||
|
expect(candidateEntity.getProps().journeys).toHaveLength(2);
|
||||||
|
expect(
|
||||||
|
(candidateEntity.getProps().journeys as Journey[])[0].journeyItems[1]
|
||||||
|
.actorTimes[0].target,
|
||||||
|
).toBe(Target.NEUTRAL);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].journeyItems[1].actorTimes[0].firstDatetime.getUTCHours(),
|
||||||
|
).toBe(0);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].journeyItems[1].actorTimes[0].firstDatetime.getUTCMinutes(),
|
||||||
|
).toBe(30);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getUTCHours(),
|
||||||
|
).toBe(23);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getUTCMinutes(),
|
||||||
|
).toBe(30);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue