add default params, time transformer
This commit is contained in:
parent
336ffe2cf5
commit
cde1760099
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export type ScheduleItem = {
|
export type ScheduleItem = {
|
||||||
day?: number;
|
day: number;
|
||||||
time: string;
|
time: string;
|
||||||
margin?: number;
|
margin: number;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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 [];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue