add default params, time transformer

This commit is contained in:
sbriat 2023-08-30 14:07:00 +02:00
parent 336ffe2cf5
commit cde1760099
16 changed files with 377 additions and 106 deletions

View File

@ -1,23 +1,8 @@
import { ExtendedRepositoryPort } from '@mobicoop/ddd-library'; import { ExtendedRepositoryPort } from '@mobicoop/ddd-library';
import { AdEntity } from '../../domain/ad.entity'; import { AdEntity } from '../../domain/ad.entity';
import { AlgorithmType, Candidate } from '../types/algorithm.types'; import { Candidate } from '../types/algorithm.types';
import { Frequency } from '../../domain/ad.types'; import { MatchQuery } from '../queries/match/match.query';
export type AdRepositoryPort = ExtendedRepositoryPort<AdEntity> & { export type AdRepositoryPort = ExtendedRepositoryPort<AdEntity> & {
getCandidates(query: CandidateQuery): Promise<Candidate[]>; getCandidates(query: MatchQuery): Promise<Candidate[]>;
};
export type CandidateQuery = {
driver: boolean;
passenger: boolean;
strict: boolean;
frequency: Frequency;
fromDate: string;
toDate: string;
schedule: {
day?: number;
time: string;
margin?: number;
}[];
algorithmType: AlgorithmType;
}; };

View File

@ -1,3 +1,5 @@
import { AlgorithmType } from '../types/algorithm.types';
export type DefaultParams = { export type DefaultParams = {
DRIVER: boolean; DRIVER: boolean;
PASSENGER: boolean; PASSENGER: boolean;
@ -5,5 +7,13 @@ export type DefaultParams = {
SEATS_REQUESTED: number; SEATS_REQUESTED: number;
DEPARTURE_TIME_MARGIN: number; DEPARTURE_TIME_MARGIN: number;
STRICT: boolean; STRICT: boolean;
DEFAULT_TIMEZONE: string; TIMEZONE: string;
ALGORITHM_TYPE: AlgorithmType;
REMOTENESS: number;
USE_PROPORTION: boolean;
PROPORTION: number;
USE_AZIMUTH: boolean;
AZIMUTH_MARGIN: number;
MAX_DETOUR_DISTANCE_RATIO: number;
MAX_DETOUR_DURATION_RATIO: number;
}; };

View File

@ -5,15 +5,44 @@ import { PassengerOrientedAlgorithm } from './passenger-oriented-algorithm';
import { AlgorithmType } from '../../types/algorithm.types'; import { AlgorithmType } from '../../types/algorithm.types';
import { Inject } from '@nestjs/common'; import { Inject } from '@nestjs/common';
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port'; import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens'; import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens';
import { MatchEntity } from '@modules/ad/core/domain/match.entity'; import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { DefaultParamsProviderPort } from '../../ports/default-params-provider.port';
import { DefaultParams } from '../../ports/default-params.type';
@QueryHandler(MatchQuery) @QueryHandler(MatchQuery)
export class MatchQueryHandler implements IQueryHandler { export class MatchQueryHandler implements IQueryHandler {
private readonly _defaultParams: DefaultParams;
constructor( constructor(
@Inject(PARAMS_PROVIDER)
private readonly defaultParamsProvider: DefaultParamsProviderPort,
@Inject(AD_REPOSITORY) private readonly repository: AdRepositoryPort, @Inject(AD_REPOSITORY) private readonly repository: AdRepositoryPort,
) {} ) {
this._defaultParams = defaultParamsProvider.getParams();
}
execute = async (query: MatchQuery): Promise<MatchEntity[]> => { execute = async (query: MatchQuery): Promise<MatchEntity[]> => {
query
.setMissingMarginDurations(this._defaultParams.DEPARTURE_TIME_MARGIN)
.setMissingStrict(this._defaultParams.STRICT)
.setDefaultDriverAndPassengerParameters({
driver: this._defaultParams.DRIVER,
passenger: this._defaultParams.PASSENGER,
seatsProposed: this._defaultParams.SEATS_PROPOSED,
seatsRequested: this._defaultParams.SEATS_REQUESTED,
})
.setDefaultAlgorithmParameters({
algorithmType: this._defaultParams.ALGORITHM_TYPE,
remoteness: 0,
useProportion: false,
proportion: 0,
useAzimuth: false,
azimuthMargin: 0,
maxDetourDistanceRatio: 0,
maxDetourDurationRatio: 0,
});
let algorithm: Algorithm; let algorithm: Algorithm;
switch (query.algorithmType) { switch (query.algorithmType) {
case AlgorithmType.PASSENGER_ORIENTED: case AlgorithmType.PASSENGER_ORIENTED:

View File

@ -1,21 +1,32 @@
import { QueryBase } from '@mobicoop/ddd-library'; import { QueryBase } from '@mobicoop/ddd-library';
import { AlgorithmType } from '../../types/algorithm.types'; import { AlgorithmType } from '../../types/algorithm.types';
import { Waypoint } from '../../types/waypoint.type'; import { Waypoint } from '../../types/waypoint.type';
import { ScheduleItem } from '../../types/schedule-item.type';
import { Frequency } from '@modules/ad/core/domain/ad.types'; import { Frequency } from '@modules/ad/core/domain/ad.types';
import { MatchRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/match.request.dto';
export class MatchQuery extends QueryBase { export class MatchQuery extends QueryBase {
readonly driver: boolean; driver?: boolean;
readonly passenger: boolean; passenger?: boolean;
readonly frequency: Frequency; readonly frequency: Frequency;
readonly fromDate: string; readonly fromDate: string;
readonly toDate: string; readonly toDate: string;
readonly schedule: ScheduleItem[]; schedule: ScheduleItem[];
readonly strict: boolean; seatsProposed?: number;
seatsRequested?: number;
strict?: boolean;
readonly waypoints: Waypoint[]; readonly waypoints: Waypoint[];
readonly algorithmType: AlgorithmType; algorithmType?: AlgorithmType;
remoteness?: number;
useProportion?: boolean;
proportion?: number;
useAzimuth?: boolean;
azimuthMargin?: number;
maxDetourDistanceRatio?: number;
maxDetourDurationRatio?: number;
readonly page?: number;
readonly perPage?: number;
constructor(props: MatchQuery) { constructor(props: MatchRequestDto) {
super(); super();
this.driver = props.driver; this.driver = props.driver;
this.passenger = props.passenger; this.passenger = props.passenger;
@ -23,8 +34,98 @@ export class MatchQuery extends QueryBase {
this.fromDate = props.fromDate; this.fromDate = props.fromDate;
this.toDate = props.toDate; this.toDate = props.toDate;
this.schedule = props.schedule; this.schedule = props.schedule;
this.seatsProposed = props.seatsProposed;
this.seatsRequested = props.seatsRequested;
this.strict = props.strict; this.strict = props.strict;
this.waypoints = props.waypoints; this.waypoints = props.waypoints;
this.algorithmType = props.algorithmType; this.algorithmType = props.algorithmType;
this.remoteness = props.remoteness;
this.useProportion = props.useProportion;
this.proportion = props.proportion;
this.useAzimuth = props.useAzimuth;
this.azimuthMargin = props.azimuthMargin;
this.maxDetourDistanceRatio = props.maxDetourDistanceRatio;
this.maxDetourDurationRatio = props.maxDetourDurationRatio;
this.page = props.page ?? 1;
this.perPage = props.perPage ?? 10;
} }
setMissingMarginDurations = (defaultMarginDuration: number): MatchQuery => {
this.schedule.forEach((day: ScheduleItem) => {
if (day.margin === undefined) day.margin = defaultMarginDuration;
});
return this;
};
setMissingStrict = (strict: boolean): MatchQuery => {
if (this.strict === undefined) this.strict = strict;
return this;
};
setDefaultDriverAndPassengerParameters = (
defaultDriverAndPassengerParameters: DefaultDriverAndPassengerParameters,
): MatchQuery => {
this.driver = !!this.driver;
this.passenger = !!this.passenger;
if (!this.driver && !this.passenger) {
this.driver = defaultDriverAndPassengerParameters.driver;
this.seatsProposed = defaultDriverAndPassengerParameters.seatsProposed;
this.passenger = defaultDriverAndPassengerParameters.passenger;
this.seatsRequested = defaultDriverAndPassengerParameters.seatsRequested;
return this;
}
if (!this.seatsProposed || this.seatsProposed <= 0)
this.seatsProposed = defaultDriverAndPassengerParameters.seatsProposed;
if (!this.seatsRequested || this.seatsRequested <= 0)
this.seatsRequested = defaultDriverAndPassengerParameters.seatsRequested;
return this;
};
setDefaultAlgorithmParameters = (
defaultAlgorithmParameters: DefaultAlgorithmParameters,
): MatchQuery => {
if (!this.algorithmType)
this.algorithmType = defaultAlgorithmParameters.algorithmType;
if (!this.remoteness)
this.remoteness = defaultAlgorithmParameters.remoteness;
if (this.useProportion == undefined)
this.useProportion = defaultAlgorithmParameters.useProportion;
if (!this.proportion)
this.proportion = defaultAlgorithmParameters.proportion;
if (this.useAzimuth == undefined)
this.useAzimuth = defaultAlgorithmParameters.useAzimuth;
if (!this.azimuthMargin)
this.azimuthMargin = defaultAlgorithmParameters.azimuthMargin;
if (!this.maxDetourDistanceRatio)
this.maxDetourDistanceRatio =
defaultAlgorithmParameters.maxDetourDistanceRatio;
if (!this.maxDetourDurationRatio)
this.maxDetourDurationRatio =
defaultAlgorithmParameters.maxDetourDurationRatio;
return this;
};
}
type ScheduleItem = {
day?: number;
time: string;
margin?: number;
};
interface DefaultDriverAndPassengerParameters {
driver: boolean;
passenger: boolean;
seatsProposed: number;
seatsRequested: number;
}
interface DefaultAlgorithmParameters {
algorithmType: AlgorithmType;
remoteness: number;
useProportion: boolean;
proportion: number;
useAzimuth: boolean;
azimuthMargin: number;
maxDetourDistanceRatio: number;
maxDetourDurationRatio: number;
} }

View File

@ -1,5 +1,5 @@
export type ScheduleItem = { export type ScheduleItem = {
day?: number; day: number;
time: string; time: string;
margin?: number; margin: number;
}; };

View File

@ -1,3 +1,5 @@
import { AlgorithmType } from '../application/types/algorithm.types';
// All properties that a Match has // All properties that a Match has
export interface MatchProps { export interface MatchProps {
adId: string; adId: string;
@ -7,3 +9,20 @@ export interface MatchProps {
export interface CreateMatchProps { export interface CreateMatchProps {
adId: string; adId: string;
} }
export interface DefaultMatchQueryProps {
driver: boolean;
passenger: boolean;
marginDuration: number;
strict: boolean;
seatsProposed: number;
seatsRequested: number;
algorithmType?: AlgorithmType;
remoteness?: number;
useProportion?: boolean;
proportion?: number;
useAzimuth?: boolean;
azimuthMargin?: number;
maxDetourDistanceRatio?: number;
maxDetourDurationRatio?: number;
}

View File

@ -10,13 +10,13 @@ import {
* */ * */
export interface ScheduleItemProps { export interface ScheduleItemProps {
day?: number; day: number;
time: string; time: string;
margin?: number; margin: number;
} }
export class ScheduleItem extends ValueObject<ScheduleItemProps> { export class ScheduleItem extends ValueObject<ScheduleItemProps> {
get day(): number | undefined { get day(): number {
return this.props.day; return this.props.day;
} }
@ -24,13 +24,13 @@ export class ScheduleItem extends ValueObject<ScheduleItemProps> {
return this.props.time; return this.props.time;
} }
get margin(): number | undefined { get margin(): number {
return this.props.margin; return this.props.margin;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected validate(props: ScheduleItemProps): void { protected validate(props: ScheduleItemProps): void {
if (props.day !== undefined && (props.day < 0 || props.day > 6)) if (props.day < 0 || props.day > 6)
throw new ArgumentOutOfRangeException('day must be between 0 and 6'); throw new ArgumentOutOfRangeException('day must be between 0 and 6');
if (props.time.split(':').length != 2) if (props.time.split(':').length != 2)
throw new ArgumentInvalidException('time is invalid'); throw new ArgumentInvalidException('time is invalid');

View File

@ -1,9 +1,6 @@
import { Inject, Injectable, Logger } from '@nestjs/common'; import { Inject, Injectable, Logger } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { import { AdRepositoryPort } from '../core/application/ports/ad.repository.port';
AdRepositoryPort,
CandidateQuery,
} from '../core/application/ports/ad.repository.port';
import { LoggerBase, MessagePublisherPort } from '@mobicoop/ddd-library'; import { LoggerBase, MessagePublisherPort } from '@mobicoop/ddd-library';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service';
import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens'; import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens';
@ -13,6 +10,7 @@ import { ExtendedPrismaRepositoryBase } from '@mobicoop/ddd-library/dist/db/pris
import { Frequency, Role } from '../core/domain/ad.types'; import { Frequency, Role } from '../core/domain/ad.types';
import { Candidate } from '../core/application/types/algorithm.types'; import { Candidate } from '../core/application/types/algorithm.types';
import { AdSelector } from './ad.selector'; import { AdSelector } from './ad.selector';
import { MatchQuery } from '../core/application/queries/match/match.query';
export type AdBaseModel = { export type AdBaseModel = {
uuid: string; uuid: string;
@ -93,7 +91,7 @@ export class AdRepository
); );
} }
getCandidates = async (query: CandidateQuery): Promise<Candidate[]> => { getCandidates = async (query: MatchQuery): Promise<Candidate[]> => {
// let candidates: Candidate[] = []; // let candidates: Candidate[] = [];
const sqlQueries: QueryRole[] = []; const sqlQueries: QueryRole[] = [];
if (query.driver) if (query.driver)
@ -115,7 +113,7 @@ export class AdRepository
} as AdsRole), } as AdsRole),
), ),
); );
console.log(results[0].ads); // console.log(results[0].ads);
return []; return [];
}; };
} }

View File

@ -1,9 +1,9 @@
import { CandidateQuery } from '../core/application/ports/ad.repository.port'; import { MatchQuery } from '../core/application/queries/match/match.query';
import { AlgorithmType } from '../core/application/types/algorithm.types'; import { AlgorithmType } from '../core/application/types/algorithm.types';
import { Role } from '../core/domain/ad.types'; import { Role } from '../core/domain/ad.types';
export class AdSelector { export class AdSelector {
static select = (role: Role, query: CandidateQuery): string => { static select = (role: Role, query: MatchQuery): string => {
switch (query.algorithmType) { switch (query.algorithmType) {
case AlgorithmType.PASSENGER_ORIENTED: case AlgorithmType.PASSENGER_ORIENTED:
default: default:

View File

@ -2,32 +2,84 @@ import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port'; import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
import { DefaultParams } from '../core/application/ports/default-params.type'; import { DefaultParams } from '../core/application/ports/default-params.type';
import { AlgorithmType } from '../core/application/types/algorithm.types';
const DEFAULT_SEATS_PROPOSED = 3; const DRIVER = false;
const DEFAULT_SEATS_REQUESTED = 1; const PASSENGER = true;
const DEFAULT_DEPARTURE_TIME_MARGIN = 900; const SEATS_PROPOSED = 3;
const DEFAULT_TIMEZONE = 'Europe/Paris'; const SEATS_REQUESTED = 1;
const DEPARTURE_TIME_MARGIN = 900;
const TIMEZONE = 'Europe/Paris';
const ALGORITHM_TYPE = 'PASSENGER_ORIENTED';
const REMOTENESS = 15000;
const USE_PROPORTION = true;
const PROPORTION = 0.3;
const USE_AZIMUTH = true;
const AZIMUTH_MARGIN = 10;
const MAX_DETOUR_DISTANCE_RATIO = 0.3;
const MAX_DETOUR_DURATION_RATIO = 0.3;
@Injectable() @Injectable()
export class DefaultParamsProvider implements DefaultParamsProviderPort { export class DefaultParamsProvider implements DefaultParamsProviderPort {
constructor(private readonly _configService: ConfigService) {} constructor(private readonly _configService: ConfigService) {}
getParams = (): DefaultParams => ({ getParams = (): DefaultParams => ({
DRIVER: this._configService.get('ROLE') == 'driver', DRIVER:
this._configService.get('ROLE') !== undefined
? this._configService.get('ROLE') == 'driver'
: DRIVER,
SEATS_PROPOSED: SEATS_PROPOSED:
this._configService.get('SEATS_PROPOSED') !== undefined this._configService.get('SEATS_PROPOSED') !== undefined
? parseInt(this._configService.get('SEATS_PROPOSED') as string) ? parseInt(this._configService.get('SEATS_PROPOSED') as string)
: DEFAULT_SEATS_PROPOSED, : SEATS_PROPOSED,
PASSENGER: this._configService.get('ROLE') == 'passenger', PASSENGER:
this._configService.get('ROLE') !== undefined
? this._configService.get('ROLE') == 'passenger'
: PASSENGER,
SEATS_REQUESTED: SEATS_REQUESTED:
this._configService.get('SEATS_REQUESTED') !== undefined this._configService.get('SEATS_REQUESTED') !== undefined
? parseInt(this._configService.get('SEATS_REQUESTED') as string) ? parseInt(this._configService.get('SEATS_REQUESTED') as string)
: DEFAULT_SEATS_REQUESTED, : SEATS_REQUESTED,
DEPARTURE_TIME_MARGIN: DEPARTURE_TIME_MARGIN:
this._configService.get('DEPARTURE_TIME_MARGIN') !== undefined this._configService.get('DEPARTURE_TIME_MARGIN') !== undefined
? parseInt(this._configService.get('DEPARTURE_TIME_MARGIN') as string) ? parseInt(this._configService.get('DEPARTURE_TIME_MARGIN') as string)
: DEFAULT_DEPARTURE_TIME_MARGIN, : DEPARTURE_TIME_MARGIN,
STRICT: this._configService.get('STRICT_FREQUENCY') == 'true', STRICT: this._configService.get('STRICT_FREQUENCY') == 'true',
DEFAULT_TIMEZONE: TIMEZONE: this._configService.get('TIMEZONE') ?? TIMEZONE,
this._configService.get('DEFAULT_TIMEZONE') ?? DEFAULT_TIMEZONE, ALGORITHM_TYPE:
AlgorithmType[
this._configService.get('ALGORITHM_TYPE') as AlgorithmType
] ?? AlgorithmType[ALGORITHM_TYPE],
REMOTENESS:
this._configService.get('REMOTENESS') !== undefined
? parseInt(this._configService.get('REMOTENESS') as string)
: REMOTENESS,
USE_PROPORTION:
this._configService.get('USE_PROPORTION') !== undefined
? this._configService.get('USE_PROPORTION') == 'true'
: USE_PROPORTION,
PROPORTION:
this._configService.get('PROPORTION') !== undefined
? parseFloat(this._configService.get('PROPORTION') as string)
: PROPORTION,
USE_AZIMUTH:
this._configService.get('USE_AZIMUTH') !== undefined
? this._configService.get('USE_AZIMUTH') == 'true'
: USE_AZIMUTH,
AZIMUTH_MARGIN:
this._configService.get('AZIMUTH_MARGIN') !== undefined
? parseInt(this._configService.get('AZIMUTH_MARGIN') as string)
: AZIMUTH_MARGIN,
MAX_DETOUR_DISTANCE_RATIO:
this._configService.get('MAX_DETOUR_DISTANCE_RATIO') !== undefined
? parseFloat(
this._configService.get('MAX_DETOUR_DISTANCE_RATIO') as string,
)
: MAX_DETOUR_DISTANCE_RATIO,
MAX_DETOUR_DURATION_RATIO:
this._configService.get('MAX_DETOUR_DURATION_RATIO') !== undefined
? parseFloat(
this._configService.get('MAX_DETOUR_DURATION_RATIO') as string,
)
: MAX_DETOUR_DURATION_RATIO,
}); });
} }

View File

@ -23,7 +23,7 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
private readonly timezoneFinder: TimezoneFinderPort, private readonly timezoneFinder: TimezoneFinderPort,
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort, @Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
) { ) {
this._defaultTimezone = defaultParamsProvider.getParams().DEFAULT_TIMEZONE; this._defaultTimezone = defaultParamsProvider.getParams().TIMEZONE;
} }
/** /**

View File

@ -2,10 +2,13 @@ import {
ArrayMinSize, ArrayMinSize,
IsArray, IsArray,
IsBoolean, IsBoolean,
IsDecimal,
IsEnum, IsEnum,
IsISO8601, IsISO8601,
IsInt, IsInt,
IsOptional, IsOptional,
Max,
Min,
ValidateNested, ValidateNested,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
@ -18,13 +21,13 @@ import { Frequency } from '@modules/ad/core/domain/ad.types';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
export class MatchRequestDto { export class MatchRequestDto {
// @IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
driver: boolean; driver?: boolean;
// @IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
passenger: boolean; passenger?: boolean;
@IsEnum(Frequency) @IsEnum(Frequency)
@HasDay('schedule', { @HasDay('schedule', {
@ -53,17 +56,17 @@ export class MatchRequestDto {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
schedule: ScheduleItemDto[]; schedule: ScheduleItemDto[];
// @IsOptional() @IsOptional()
// @IsInt() @IsInt()
// seatsProposed?: number; seatsProposed?: number;
// @IsOptional() @IsOptional()
// @IsInt() @IsInt()
// seatsRequested?: number; seatsRequested?: number;
// @IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
strict: boolean; strict?: boolean;
@Type(() => WaypointDto) @Type(() => WaypointDto)
@IsArray() @IsArray()
@ -72,45 +75,45 @@ export class MatchRequestDto {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
waypoints: WaypointDto[]; waypoints: WaypointDto[];
// @IsOptional() @IsOptional()
@IsEnum(AlgorithmType) @IsEnum(AlgorithmType)
algorithmType: AlgorithmType; algorithmType?: AlgorithmType;
// @IsOptional() @IsOptional()
// @IsInt() @IsInt()
// remoteness?: number; remoteness?: number;
// @IsOptional() @IsOptional()
// @IsBoolean() @IsBoolean()
// useProportion?: boolean; useProportion?: boolean;
// @IsOptional() @IsOptional()
// @IsDecimal() @IsDecimal()
// @Min(0) @Min(0)
// @Max(1) @Max(1)
// proportion?: number; proportion?: number;
// @IsOptional() @IsOptional()
// @IsBoolean() @IsBoolean()
// useAzimuth?: boolean; useAzimuth?: boolean;
// @IsOptional() @IsOptional()
// @IsInt() @IsInt()
// @Min(0) @Min(0)
// @Max(359) @Max(359)
// azimuthMargin?: number; azimuthMargin?: number;
// @IsOptional() @IsOptional()
// @IsDecimal() @IsDecimal()
// @Min(0) @Min(0)
// @Max(1) @Max(1)
// maxDetourDistanceRatio?: number; maxDetourDistanceRatio?: number;
// @IsOptional() @IsOptional()
// @IsDecimal() @IsDecimal()
// @Min(0) @Min(0)
// @Max(1) @Max(1)
// maxDetourDurationRatio?: number; maxDetourDurationRatio?: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()

View File

@ -1,9 +1,10 @@
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens'; import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens';
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query'; import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
import { MatchQueryHandler } from '@modules/ad/core/application/queries/match/match.query-handler'; import { MatchQueryHandler } from '@modules/ad/core/application/queries/match/match.query-handler';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type'; import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
import { Frequency } from '@modules/ad/core/domain/ad.types'; import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity'; import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
@ -27,7 +28,36 @@ const destinationWaypoint: Waypoint = {
}; };
const mockAdRepository = { const mockAdRepository = {
getCandidates: jest.fn(), getCandidates: jest.fn().mockImplementation(() => [
{
ad: {
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
},
role: Role.DRIVER,
},
]),
};
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
getParams: () => {
return {
DEPARTURE_TIME_MARGIN: 900,
DRIVER: false,
SEATS_PROPOSED: 3,
PASSENGER: true,
SEATS_REQUESTED: 1,
STRICT: false,
TIMEZONE: 'Europe/Paris',
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
REMOTENESS: 15000,
USE_PROPORTION: true,
PROPORTION: 0.3,
USE_AZIMUTH: true,
AZIMUTH_MARGIN: 10,
MAX_DETOUR_DISTANCE_RATIO: 0.3,
MAX_DETOUR_DURATION_RATIO: 0.3,
};
},
}; };
describe('Match Query Handler', () => { describe('Match Query Handler', () => {
@ -41,6 +71,10 @@ describe('Match Query Handler', () => {
provide: AD_REPOSITORY, provide: AD_REPOSITORY,
useValue: mockAdRepository, useValue: mockAdRepository,
}, },
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
},
], ],
}).compile(); }).compile();

View File

@ -3,7 +3,7 @@ import { MatchQuery } from '@modules/ad/core/application/queries/match/match.que
import { PassengerOrientedAlgorithm } from '@modules/ad/core/application/queries/match/passenger-oriented-algorithm'; import { PassengerOrientedAlgorithm } from '@modules/ad/core/application/queries/match/passenger-oriented-algorithm';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type'; import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
import { Frequency } from '@modules/ad/core/domain/ad.types'; import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity'; import { MatchEntity } from '@modules/ad/core/domain/match.entity';
const originWaypoint: Waypoint = { const originWaypoint: Waypoint = {
@ -52,7 +52,14 @@ const mockMatcherRepository: AdRepositoryPort = {
count: jest.fn(), count: jest.fn(),
healthCheck: jest.fn(), healthCheck: jest.fn(),
queryRawUnsafe: jest.fn(), queryRawUnsafe: jest.fn(),
getCandidates: jest.fn(), getCandidates: jest.fn().mockImplementation(() => [
{
ad: {
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
},
role: Role.DRIVER,
},
]),
}; };
describe('Passenger oriented algorithm', () => { describe('Passenger oriented algorithm', () => {

View File

@ -16,8 +16,24 @@ const mockConfigService = {
return 1; return 1;
case 'STRICT_FREQUENCY': case 'STRICT_FREQUENCY':
return 'false'; return 'false';
case 'DEFAULT_TIMEZONE': case 'TIMEZONE':
return 'Europe/Paris'; return 'Europe/Paris';
case 'ALGORITHM_TYPE':
return 'PASSENGER_ORIENTED';
case 'REMOTENESS':
return 15000;
case 'USE_PROPORTION':
return 'true';
case 'PROPORTION':
return 0.3;
case 'USE_AZIMUTH':
return 'true';
case 'AZIMUTH_MARGIN':
return 10;
case 'MAX_DETOUR_DISTANCE_RATIO':
return 0.3;
case 'MAX_DETOUR_DURATION_RATIO':
return 0.3;
default: default:
return 'some_default_value'; return 'some_default_value';
} }
@ -53,6 +69,14 @@ describe('DefaultParamsProvider', () => {
expect(params.DEPARTURE_TIME_MARGIN).toBe(900); expect(params.DEPARTURE_TIME_MARGIN).toBe(900);
expect(params.PASSENGER).toBeTruthy(); expect(params.PASSENGER).toBeTruthy();
expect(params.DRIVER).toBeFalsy(); expect(params.DRIVER).toBeFalsy();
expect(params.DEFAULT_TIMEZONE).toBe('Europe/Paris'); expect(params.TIMEZONE).toBe('Europe/Paris');
expect(params.ALGORITHM_TYPE).toBe('PASSENGER_ORIENTED');
expect(params.REMOTENESS).toBe(15000);
expect(params.USE_PROPORTION).toBeTruthy();
expect(params.PROPORTION).toBe(0.3);
expect(params.USE_AZIMUTH).toBeTruthy();
expect(params.AZIMUTH_MARGIN).toBe(10);
expect(params.MAX_DETOUR_DISTANCE_RATIO).toBe(0.3);
expect(params.MAX_DETOUR_DURATION_RATIO).toBe(0.3);
}); });
}); });

View File

@ -7,6 +7,7 @@ import { Frequency } from '@modules/ad/core/application/ports/datetime-transform
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port'; import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port'; import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port'; import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer'; import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
@ -19,7 +20,15 @@ const mockDefaultParamsProvider: DefaultParamsProviderPort = {
PASSENGER: true, PASSENGER: true,
SEATS_REQUESTED: 1, SEATS_REQUESTED: 1,
STRICT: false, STRICT: false,
DEFAULT_TIMEZONE: 'Europe/Paris', TIMEZONE: 'Europe/Paris',
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
REMOTENESS: 15000,
USE_PROPORTION: true,
PROPORTION: 0.3,
USE_AZIMUTH: true,
AZIMUTH_MARGIN: 10,
MAX_DETOUR_DISTANCE_RATIO: 0.3,
MAX_DETOUR_DURATION_RATIO: 0.3,
}; };
}, },
}; };