# Mobicoop V3 - Matcher Mobility matching service for Mobicoop V3. ## Requirements You need [Docker](https://docs.docker.com/engine/) and its [compose](https://docs.docker.com/compose/) plugin. You also need NodeJS installed locally : we **strongly** advise to install [Node Version Manager](https://github.com/nvm-sh/nvm) and use the latest LTS version of Node (check that your local version matches with the one used in the Dockerfile). The API will run inside a docker container, **but** the install itself is made outside the container, because during development we need tools that need to be available locally (eg. ESLint or Prettier with fix-on-save). A RabbitMQ instance is also required to send / receive messages when data has been inserted/updated/deleted. # Installation - copy `.env.dist` to `.env` : ```bash cp .env.dist .env ``` Modify it if needed. - install the dependencies : ```bash npm install ``` - start the containers : ```bash docker compose up -d ``` The app runs automatically on port **5005**. ## Database migration Before using the app, you need to launch the database migration (it will be launched inside the container) : ```bash npm run migrate ``` ## Usage The app exposes the following [gRPC](https://grpc.io/) services : - **Match** : find matching ads corresponding to the given criteria For example, as a passenger, to search for drivers for a punctual carpool : ```json { "driver": false, "passenger": true, "frequency": "PUNCTUAL", "algorithmType": "PASSENGER_ORIENTED", "fromDate": "2024-06-05", "toDate": "2024-06-05", "schedule": [ { "time": "07:30" } ], "waypoints": [ { "houseNumber": "23", "street": "rue de viller", "postalCode": "54300", "locality": "Lunéville", "lon": 6.490527, "lat": 48.590119, "country": "France", "position": 0 }, { "houseNumber": "3", "street": "rue du passage", "postalCode": "67117", "locality": "Ittenheim", "lon": 7.594361, "lat": 48.603004, "country": "France", "position": 1 } ] } ``` As a passenger, to search for drivers for a recurrent carpool : ```json { "driver": false, "passenger": true, "frequency": "RECURRENT", "algorithmType": "PASSENGER_ORIENTED", "fromDate": "2024-01-02", "toDate": "2024-06-30", "strict": true, "page": 1, "perPage": 5, "schedule": [ { "day": 1, "time": "07:30" }, { "day": 2, "time": "07:45" }, { "day": 4, "time": "07:30" }, , { "day": 5, "time": "07:30" } ], "waypoints": [ { "houseNumber": "298", "street": "Aveue de la liberté", "postalCode": "86180", "locality": "Buxerolles", "lon": 0.364394, "lat": 46.607501, "country": "France", "position": 0 }, { "houseNumber": "1", "street": "place du 8 mai 1945", "postalCode": "47310", "locality": "Roquefort", "lon": 0.559606, "lat": 44.175994, "country": "France", "position": 1 } ] } ``` The list of possible criteria : - **id** (optional): the id of a previous matching result (as a uuid) - **driver** (boolean, optional): to search for passengers (_default : false_) - **passenger** (boolean, optional): to search fo drivers (_default : true_) - **frequency**: the frequency of the search (`PUNCTUAL` or `RECURRENT`) - **strict** (boolean, optional): if set to true, allow matching only with similar frequency ads (_default : false_) - **fromDate**: start date for recurrent ad, carpool date for punctual ad - **toDate**: end date for recurrent ad, same as fromDate for punctual ad - **schedule**: an array of schedule items, a schedule item containing : - the week day as a number, from 0 (sunday) to 6 (saturday) if the ad is recurrent (default to fromDate day for punctual search) - the departure time (as HH:MM) - the margin around the departure time in seconds (optional) (_default : 900_) - **seatsProposed** (integer, optional): number of seats proposed as driver (_default : 3_) - **seatsRequested** (integer, optional): number of seats requested as passenger (_default : 1_) - **waypoints**: an array of addresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads). Note that positions are **required** and **must** be consecutives - **algorithmType** (optional): the type of algorithm to use (as of 2023-09-28, only the default `PASSENGER_ORIENTED` is accepted) - **remoteness** (integer, optional): an integer to indicate the maximum flying distance (in metres) between the driver route and the passenger pick-up / drop-off points (_default : 15000_) - **useProportion** (boolean, optional): a boolean to indicate if the matching algorithm will compare the distance of the passenger route against the distance of the driver route (_default : 1_). Works in combination with **proportion** parameter - **proportion** (float, optional): a fraction (float between 0 and 1) to indicate minimum proportion of the distance of the passenger route against the distance of the driver route (_default : 0.3_). Works in combination with **use_proportion** parameter - **useAzimuth** (boolean, optional): a boolean to indicate if the matching algorithm will use the azimuth of the driver and passenger routes (_default : 1_) - **azimuthMargin** (integer, optional): an integer (representing the number of degrees) to indicate the range around the opposite azimuth to consider the candidate route excluded (_default : 10_) - **maxDetourDistanceRatio** (float, optional): a fraction (float between 0 and 1) of the driver route distance to indicate the maximum detour distance acceptable for a passenger (_default : 0.3_) - **maxDetourDurationRatio** (float, optional): a fraction (float between 0 and 1) of the driver route duration to indicate the maximum detour duration acceptable for a passenger (_default : 0.3_) - **page** (integer, optional): the page of results to display (_default : 1_) - **perPage** (integer, optional): the number of results to display per page (_default : 10_) If the matching is successful, you will get a result, containing : - **id**: the id of the matching; as matching is a time-consuming process, results are cached and thus accessible later using this id (pagination works as well !) - **total**: the total number of results - **page**: the number of the page that is returned (may be different than the number of the required page, if number of results does not match with perPage parameter) - **perPage**: the number of results per page (as it may not be specified in the request) - **data**: an array of the results themselves, each including: - **id**: an id for the result - **adId**: the id of the ad that matches - **role**: the role of the ad owner in that match - **distance**: the distance in metres of the resulting carpool - **duration**: the duration in seconds of the resulting carpool - **initialDistance**: the initial distance in metres for the driver - **initialDuration**: the initial duration in seconds for the driver - **distanceDetour**: the detour distance in metres - **durationDetour**: the detour duration in seconds - **distanceDetourPercentage**: the detour distance in percentage of the original distance - **durationDetourPercentage**: the detour duration in percentage of the original duration - **journeys**: the possible journeys for the carpool (one journey for punctual carpools, one or more journeys for recurrent carpools), each including: - **day**: the week day for the journey, as a number, from 0 (sunday) to 6 (saturday) - **firstDate**: the first possible date for the journey - **lastDate**: the last possible date for the journey - **steps**: the steps of the journey (coordinates with distance, duration and actors implied), each including: - **distance**: the distance to reach the step in metres - **duration**: the duration to reach the step in seconds - **lon**: the longitude of the point for the step - **lat**: the longitude of the point for the step - **time**: the driver time at that step - **actors**: the actors for that step: - **role**: the role of the actor (`DRIVER` or `PASSENGER`) - **target**: the meaning of the step for the actor: - _START_ for the first point of the actor - _FINISH_ for the last point of the actor - _INTERMEDIATE_ for a driver intermediate point - _NEUTRAL_ for a passenger point from the point of view of a driver ## Tests / ESLint / Prettier Tests are run outside the container for ease of use (switching between different environments inside containers using prisma is complicated and error prone). The integration tests use a dedicated database (see _db-test_ section of _docker-compose.yml_). ```bash # run all tests (unit + integration) npm run test # unit tests only npm run test:unit # integration tests only npm run test:integration # coverage npm run test:cov # ESLint npm run lint # Prettier npm run pretty ``` ## License Mobicoop V3 - Matcher Service is [AGPL licensed](LICENSE).