Complete backend
This commit is contained in:
parent
06b2078e4d
commit
4379b4d231
@ -1,17 +0,0 @@
|
||||
# start
|
||||
|
||||
By creating a new web app that you will call Dijkstra, I would like you to:
|
||||
|
||||
- Find a DB modelization that allows you to store any country given its cities and its roads between them (see Belgian graph);
|
||||
|
||||
- Store the Belgian data (see belgium.rb);
|
||||
|
||||
- Allow a user to select Belgium as a country (or if he/she navigates to /countries/belgium), then allow to select a starting point (a Belgian city) and a destination (also a Belgian city);
|
||||
|
||||
- Once the user has selected the starting point and the destination, tell the shortest distance between those two cities;
|
||||
|
||||
- Describe to the user the path (for example: Bruges -> Ghent -> Brussels -> Liège) that corresponds to the shortest distance you returned to the user;
|
||||
|
||||
- Make your code shines!
|
||||
|
||||
# end
|
22
api/models/citiesModel.js
Normal file
22
api/models/citiesModel.js
Normal file
@ -0,0 +1,22 @@
|
||||
const db = require('../../data/dbConfig');
|
||||
|
||||
module.exports = {
|
||||
getCities,
|
||||
getCitiesByCountry
|
||||
// getCity,
|
||||
};
|
||||
|
||||
function getCities() {
|
||||
return db('cities')
|
||||
}
|
||||
|
||||
function getCitiesByCountry(country_id) {
|
||||
return db('cities')
|
||||
.where({ country_id })
|
||||
}
|
||||
|
||||
// function getCity(name) {
|
||||
// return db('cities')
|
||||
// .where({ name })
|
||||
// .first()
|
||||
// }
|
@ -1,4 +1,4 @@
|
||||
const db = require('../data/dbConfig');
|
||||
const db = require('../../data/dbConfig');
|
||||
|
||||
module.exports = {
|
||||
getCountries,
|
9
api/models/roadsModel.js
Normal file
9
api/models/roadsModel.js
Normal file
@ -0,0 +1,9 @@
|
||||
const db = require('../../data/dbConfig');
|
||||
|
||||
module.exports = {
|
||||
getRoads
|
||||
};
|
||||
|
||||
function getRoads() {
|
||||
return db('roads')
|
||||
}
|
14
api/routes/citiesRouter.js
Normal file
14
api/routes/citiesRouter.js
Normal file
@ -0,0 +1,14 @@
|
||||
const router = require('express').Router();
|
||||
|
||||
const Cities = require('../models/citiesModel');
|
||||
|
||||
router.get('', async (req, res) => {
|
||||
try {
|
||||
const cities = await Cities.getCities();
|
||||
res.status(200).json(cities);
|
||||
} catch (e) {
|
||||
res.status(500).json(e);
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
14
api/routes/countriesRouter.js
Normal file
14
api/routes/countriesRouter.js
Normal file
@ -0,0 +1,14 @@
|
||||
const router = require('express').Router();
|
||||
const Cities = require('../models/citiesModel');
|
||||
|
||||
router.get('/:country_id', async (req, res) => {
|
||||
const { country_id } = req.params;
|
||||
try {
|
||||
const cities = await Cities.getCitiesByCountry(country_id);
|
||||
res.status(200).json(cities);
|
||||
} catch (e) {
|
||||
res.status(500).json(e);
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
40
api/routes/pathRouter.js
Normal file
40
api/routes/pathRouter.js
Normal file
@ -0,0 +1,40 @@
|
||||
const router = require('express').Router();
|
||||
|
||||
const Roads = require('../models/roadsModel');
|
||||
const Cities = require('../models/citiesModel');
|
||||
|
||||
const searchPath = require('../../helpers/dijkstra_algo');
|
||||
|
||||
router.get('', async (req, res) => {
|
||||
const { start_city_id, end_city_id } = req.body;
|
||||
|
||||
try {
|
||||
const cities = await Cities.getCities();
|
||||
const roads = await Roads.getRoads();
|
||||
|
||||
let { path, distance } = searchPath(cities, roads, start_city_id, end_city_id);
|
||||
const formatedPath = formatPath(path, cities)
|
||||
|
||||
let response = { path: formatedPath, distance }
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(e);
|
||||
}
|
||||
})
|
||||
|
||||
function formatPath(path, cities) {
|
||||
const complete_path = []
|
||||
|
||||
for (let path_city of path) {
|
||||
for (let city of cities) {
|
||||
if (city.id == path_city) {
|
||||
complete_path.push({ id: city.id, name: city.name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return complete_path
|
||||
}
|
||||
|
||||
module.exports = router;
|
13
api/routes/roadsRouter.js
Normal file
13
api/routes/roadsRouter.js
Normal file
@ -0,0 +1,13 @@
|
||||
const router = require('express').Router();
|
||||
const Roads = require('../models/roadsModel');
|
||||
|
||||
router.get('', async (req, res) => {
|
||||
try {
|
||||
const roads = await Roads.getRoads();
|
||||
res.status(200).json(roads);
|
||||
} catch (e) {
|
||||
res.status(500).json(e);
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
@ -1,17 +1,18 @@
|
||||
const express = require("express");
|
||||
|
||||
// const authRouter = require('../services/auth/authRouter');
|
||||
// const usersRouter = require('../services/users/usersRouter');
|
||||
// const sessionsRouter = require('../services/sessions/sessionsRouter');
|
||||
// const dailyAveragesRouter = require('../services/dailyAverages/dailyAveragesRouter');
|
||||
const citiesRouter = require('./routes/citiesRouter');
|
||||
const roadsRouter = require('./routes/roadsRouter');
|
||||
const countriesRouter = require('./routes/countriesRouter');
|
||||
const pathRouter = require('./routes/pathRouter')
|
||||
|
||||
|
||||
const server = express();
|
||||
|
||||
server.use(express.json());
|
||||
|
||||
// server.use('/api/auth', authRouter);
|
||||
// server.use('/api/users', authenticate, usersRouter);
|
||||
// server.use('/api/users', authenticate, sessionsRouter);
|
||||
// server.use('/api/users', authenticate, dailyAveragesRouter);
|
||||
server.use('/api/cities', citiesRouter);
|
||||
server.use('/api/roads', roadsRouter);
|
||||
server.use('/api/countries', countriesRouter);
|
||||
server.use('/api/path', pathRouter);
|
||||
|
||||
module.exports = server;
|
||||
|
50
data/test_data/cities.js
Normal file
50
data/test_data/cities.js
Normal file
@ -0,0 +1,50 @@
|
||||
module.exports = [{
|
||||
"id": 1,
|
||||
"name": 'bruges',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": 'antwerp',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": 'ghent',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": 'mechelen',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": 'brussels',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": 'mons',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": 'namur',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": 'liege',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": 'arlon',
|
||||
"country_id": 1
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": 'tournai',
|
||||
"country_id": 1
|
||||
}]
|
84
data/test_data/roads.js
Normal file
84
data/test_data/roads.js
Normal file
@ -0,0 +1,84 @@
|
||||
module.exports = [{
|
||||
"id": 1,
|
||||
"start_city_id": 1,
|
||||
"end_city_id": 3,
|
||||
"distance": 50
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"start_city_id": 3,
|
||||
"end_city_id": 10,
|
||||
"distance": 80
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"start_city_id": 10,
|
||||
"end_city_id": 5,
|
||||
"distance": 89
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"start_city_id": 3,
|
||||
"end_city_id": 5,
|
||||
"distance": 56
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"start_city_id": 3,
|
||||
"end_city_id": 2,
|
||||
"distance": 60
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"start_city_id": 2,
|
||||
"end_city_id": 4,
|
||||
"distance": 25
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"start_city_id": 4,
|
||||
"end_city_id": 5,
|
||||
"distance": 27
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"start_city_id": 5,
|
||||
"end_city_id": 6,
|
||||
"distance": 80
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"start_city_id": 6,
|
||||
"end_city_id": 7,
|
||||
"distance": 91
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"start_city_id": 6,
|
||||
"end_city_id": 10,
|
||||
"distance": 51
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"start_city_id": 7,
|
||||
"end_city_id": 9,
|
||||
"distance": 129
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"start_city_id": 9,
|
||||
"end_city_id": 7,
|
||||
"distance": 123
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"start_city_id": 8,
|
||||
"end_city_id": 7,
|
||||
"distance": 65
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"start_city_id": 8,
|
||||
"end_city_id": 5,
|
||||
"distance": 97
|
||||
}]
|
55
helpers/dijkstra_algo.js
Normal file
55
helpers/dijkstra_algo.js
Normal file
@ -0,0 +1,55 @@
|
||||
const Queue = require('./queue');
|
||||
const Graph = require('./graph');
|
||||
|
||||
function findPathWithDijkstra(cities, roads, startNode, endNode) {
|
||||
const graph = new Graph();
|
||||
|
||||
// Add cities and roads to graph
|
||||
for (let city of cities) {
|
||||
graph.addNode(city.id)
|
||||
}
|
||||
|
||||
for (let { start_city_id, end_city_id, distance } of roads) {
|
||||
graph.addEdge(start_city_id, end_city_id, distance)
|
||||
}
|
||||
|
||||
// Dijkstra path search algo
|
||||
let times = {};
|
||||
let backtrace = {};
|
||||
let queue = new Queue();
|
||||
|
||||
times[startNode] = 0;
|
||||
|
||||
graph.nodes.forEach(node => {
|
||||
if (node !== startNode) {
|
||||
times[node] = Infinity
|
||||
}
|
||||
});
|
||||
|
||||
queue.enqueue([startNode, 0]);
|
||||
|
||||
while (queue.size()) {
|
||||
let shortestStep = queue.dequeue();
|
||||
let currentNode = shortestStep[0];
|
||||
graph.adjacencyList[currentNode].forEach(neighbor => {
|
||||
let time = times[currentNode] + neighbor.distance;
|
||||
|
||||
if (time < times[neighbor.node]) {
|
||||
times[neighbor.node] = time;
|
||||
backtrace[neighbor.node] = currentNode;
|
||||
queue.enqueue([neighbor.node, time]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let path = [endNode];
|
||||
let lastStep = endNode; while(lastStep !== startNode) {
|
||||
path.unshift(backtrace[lastStep])
|
||||
lastStep = backtrace[lastStep]
|
||||
}
|
||||
|
||||
// return `Path is ${path} and time is ${times[endNode]}`
|
||||
return {path, distance: times[endNode]}
|
||||
}
|
||||
|
||||
module.exports = findPathWithDijkstra;
|
18
helpers/graph.js
Normal file
18
helpers/graph.js
Normal file
@ -0,0 +1,18 @@
|
||||
class Graph {
|
||||
constructor() {
|
||||
this.nodes = [];
|
||||
this.adjacencyList = {};
|
||||
}
|
||||
|
||||
addNode(node) {
|
||||
this.nodes.push(node);
|
||||
this.adjacencyList[node] = [];
|
||||
}
|
||||
|
||||
addEdge(node1, node2, distance) {
|
||||
this.adjacencyList[node1].push({node:node2, distance});
|
||||
this.adjacencyList[node2].push({node:node1, distance});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Graph;
|
21
helpers/queue.js
Normal file
21
helpers/queue.js
Normal file
@ -0,0 +1,21 @@
|
||||
class Queue {
|
||||
constructor() {
|
||||
this.store = [];
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.store.length
|
||||
}
|
||||
|
||||
enqueue(node) {
|
||||
this.store.push(node);
|
||||
}
|
||||
|
||||
dequeue() {
|
||||
if (this.size() > 0) {
|
||||
return this.store.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Queue;
|
18
helpers/test_dijkstra.js
Normal file
18
helpers/test_dijkstra.js
Normal file
@ -0,0 +1,18 @@
|
||||
const searchPath = require('../helpers/dijkstra_algo');
|
||||
const cities = require('../data/test_data/cities');
|
||||
const roads = require('../data/test_data/roads');
|
||||
|
||||
let { path, distance} = searchPath(cities, roads, 1, 9);
|
||||
|
||||
const formatedPath = [];
|
||||
|
||||
// I didn't use map or filter because it doesn't preserve the order
|
||||
for (let path_city of path) {
|
||||
for (let city of cities) {
|
||||
if (city.id == path_city) {
|
||||
formatedPath.push({ id: city.id, name: city.name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(formatedPath)
|
807
package-lock.json
generated
807
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,9 +9,11 @@
|
||||
"nodejs": "^0.0.0",
|
||||
"sqlite3": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "test",
|
||||
"watch": "nodemon index.js",
|
||||
"start": "node index.js"
|
||||
},
|
||||
"author": "",
|
||||
|
Loading…
Reference in New Issue
Block a user