Compare commits

..

No commits in common. "master" and "original-submission" have entirely different histories.

21 changed files with 184 additions and 1470 deletions

17
Instructions.txt Normal file
View File

@ -0,0 +1,17 @@
# 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

138
README.md
View File

@ -1,138 +0,0 @@
# dijkstra-backend
Find the the shortest path between different cities in Belgium with Dijkstra algorithm.
- Deployed Frontend: https://dijkstra.ruihildt.xyz/
- Deployed Backend: https://dijkstra-backend.ruihildt.xyz/
- Frontend Source: https://git.ruihildt.xyz/ruihildt/dijkstra-frontend
## API Documentation
___
> Values required in **`bold`**.
### COUNTRIES **`countries`**
| field | data type | metadata |
| :--------| :---------------- | :-------------------------------------------------- |
| id | unsigned integer | primary key, auto-increments, generated by database |
| name | string | |
#### Get a list of all cities by country id
**`GET /api/countries/:country_id`**
##### Response
An array of json objects with a list of cities in the selected country with `name`, `id` and `country_id`.
```
[
{
"id": 1,
"name": "bruges",
"country_id": 1
},
{
"id": 2,
"name": "antwerp",
"country_id": 1
},
{
"id": 3,
"name": "ghent",
"country_id": 1
},
]
```
### CITIES **`cities`**
| field | data type | metadata |
| :----------| :--------------------- | :-------------------------------------------------- |
| id | unsigned integer | primary key, auto-increments, generated by database |
| name | string | |
| country_id | unsigned integer | foreign key referencing `countries.id` |
#### Get a list of all cities
**`GET /api/cities`**
##### Response
An array of json objects with `id`, `name` and `country_id`.
```
[
{
"id": 1,
"name": 'bruges',
"country_id": 1
}
]
```
### ROADS **`roads`**
| field | data type | metadata |
| :-------------| :--------------- | :-------------------------------------------------- |
| id | unsigned integer | primary key, auto-increments, generated by database |
| start_city_id | string | required |
| end_city_id | string | required |
| distance | unsigned integer | |
#### Get roads list
**`GET /api/roads`**
##### Response
A array of json objects with `id`, `start_city_id`, 'end_city_id` and `distance`.
```
[
{
"id": 1,
"start_city_id": 1,
"end_city_id": 3,
"distance": 50
}
]
```
### Shortest Path **`cities`**
#### Get the shortest path between two cities and the total distance
**`GET /api/path`**
##### Request
Add the **`start_city_id`** and **`end_city_id`** as query strings to the url.
```
/api/path?start_city_id=2?end_city_id=9
```
##### Response
A json object composed of the `path` correctly ordered and the total `distance`.
```
{
"path": [
{
"id": 2,
"name": "antwerp"
},
{
"id": 4,
"name": "mechelen"
},
{
"id": 5,
"name": "brussels"
},
{
"id": 8,
"name": "liege"
},
{
"id": 7,
"name": "namur"
},
{
"id": 9,
"name": "arlon"
}
],
"distance": 337
}
```

View File

@ -1,22 +0,0 @@
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()
// }

View File

@ -1,9 +0,0 @@
const db = require('../../data/dbConfig');
module.exports = {
getRoads
};
function getRoads() {
return db('roads')
}

View File

@ -1,14 +0,0 @@
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;

View File

@ -1,14 +0,0 @@
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;

View File

@ -1,43 +0,0 @@
const router = require('express').Router();
const Roads = require('../models/roadsModel');
const Cities = require('../models/citiesModel');
const findShortestPath = require('../../helpers/dijkstra_algo');
router.get('', async (req, res) => {
const { start_city_id, end_city_id } = req.query;
start = Math.floor(start_city_id);
end = Math.floor(end_city_id);
try {
const cities = await Cities.getCities();
const roads = await Roads.getRoads();
let { path, distance } = findShortestPath(cities, roads, start, end);
const shortestPath = formatPath(path, cities)
let response = { path: shortestPath, 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;

View File

@ -1,13 +0,0 @@
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;

View File

@ -1,21 +1,17 @@
const express = require("express"); const express = require("express");
const helmet = require('helmet');
const cors = require('cors');
const citiesRouter = require('./routes/citiesRouter'); // const authRouter = require('../services/auth/authRouter');
const roadsRouter = require('./routes/roadsRouter'); // const usersRouter = require('../services/users/usersRouter');
const countriesRouter = require('./routes/countriesRouter'); // const sessionsRouter = require('../services/sessions/sessionsRouter');
const pathRouter = require('./routes/pathRouter') // const dailyAveragesRouter = require('../services/dailyAverages/dailyAveragesRouter');
const server = express(); const server = express();
server.use(helmet());
server.use(express.json()); server.use(express.json());
server.use(cors());
server.use('/api/cities', citiesRouter); // server.use('/api/auth', authRouter);
server.use('/api/roads', roadsRouter); // server.use('/api/users', authenticate, usersRouter);
server.use('/api/countries', countriesRouter); // server.use('/api/users', authenticate, sessionsRouter);
server.use('/api/path', pathRouter); // server.use('/api/users', authenticate, dailyAveragesRouter);
module.exports = server; module.exports = server;

Binary file not shown.

View File

@ -1,58 +1,59 @@
exports.seed = function (knex) {
return knex('cities') exports.seed = function(knex) {
.truncate() return knex('cities').truncate()
.then(function () { .then(function () {
return knex('cities').insert([ return knex('cities').insert([
{ {
id: 1, "id": 1,
name: 'bruges', "name": 'bruges',
country_id: 1, "country_id": 1
}, },
{ {
id: 2, "id": 2,
name: 'antwerp', "name": 'antwerp',
country_id: 1, "country_id": 1
}, },
{ {
id: 3, "id": 3,
name: 'ghent', "name": 'ghent',
country_id: 1, "country_id": 1
}, },
{ {
id: 4, "id": 4,
name: 'mechelen', "name": 'mechelen',
country_id: 1, "country_id": 1
}, },
{ {
id: 5, "id": 5,
name: 'brussels', "name": 'brussels',
country_id: 1, "country_id": 1
}, },
{ {
id: 6, "id": 6,
name: 'mons', "name": 'mons',
country_id: 1, "country_id": 1
}, },
{ {
id: 7, "id": 7,
name: 'namur', "name": 'namur',
country_id: 1, "country_id": 1
}, },
{ {
id: 8, "id": 8,
name: 'liege', "name": 'liege',
country_id: 1, "country_id": 1
}, },
{ {
id: 9, "id": 9,
name: 'arlon', "name": 'arlon',
country_id: 1, "country_id": 1
}, },
{ {
id: 10, "id": 10,
name: 'tournai', "name": 'tournai',
country_id: 1, "country_id": 1
}, },
]); ]);
}); });
}; };

View File

@ -1,92 +1,92 @@
exports.seed = function (knex) {
return knex('roads') exports.seed = function(knex) {
.truncate() return knex('roads').truncate()
.then(function () { .then(function () {
return knex('roads').insert([ return knex('roads').insert([
{ {
id: 1, "id": 1,
start_city_id: 1, "start_city_id": 1,
end_city_id: 3, "end_city_id": 3,
distance: 40, "distance": 50
}, },
{ {
id: 2, "id": 2,
start_city_id: 3, "start_city_id": 3,
end_city_id: 10, "end_city_id": 10,
distance: 60, "distance": 80
}, },
{ {
id: 3, "id": 3,
start_city_id: 10, "start_city_id": 10,
end_city_id: 5, "end_city_id": 5,
distance: 70, "distance": 89
}, },
{ {
id: 4, "id": 4,
start_city_id: 3, "start_city_id": 3,
end_city_id: 5, "end_city_id": 5,
distance: 50, "distance": 56
}, },
{ {
id: 5, "id": 5,
start_city_id: 3, "start_city_id": 3,
end_city_id: 2, "end_city_id": 2,
distance: 50, "distance": 60
}, },
{ {
id: 6, "id": 6,
start_city_id: 2, "start_city_id": 2,
end_city_id: 4, "end_city_id": 4,
distance: 20, "distance": 25
}, },
{ {
id: 7, "id": 7,
start_city_id: 4, "start_city_id": 4,
end_city_id: 5, "end_city_id": 5,
distance: 20, "distance": 27
}, },
{ {
id: 8, "id": 8,
start_city_id: 5, "start_city_id": 5,
end_city_id: 6, "end_city_id": 6,
distance: 50, "distance": 80
}, },
{ {
id: 9, "id": 9,
start_city_id: 6, "start_city_id": 6,
end_city_id: 7, "end_city_id": 7,
distance: 60, "distance": 91
}, },
{ {
id: 10, "id": 10,
start_city_id: 6, "start_city_id": 6,
end_city_id: 10, "end_city_id": 10,
distance: 40, "distance": 51
}, },
{ {
id: 11, "id": 11,
start_city_id: 7, "start_city_id": 7,
end_city_id: 9, "end_city_id": 9,
distance: 110, "distance": 129
}, },
{ {
id: 12, "id": 12,
start_city_id: 9, "start_city_id": 9,
end_city_id: 7, "end_city_id": 7,
distance: 110, "distance": 123
}, },
{ {
id: 13, "id": 13,
start_city_id: 8, "start_city_id": 8,
end_city_id: 7, "end_city_id": 7,
distance: 50, "distance": 65
}, },
{ {
id: 14, "id": 14,
start_city_id: 8, "start_city_id": 8,
end_city_id: 5, "end_city_id": 5,
distance: 90, "distance": 97
}, }
]); ]);
}); });
}; };

View File

@ -1,55 +0,0 @@
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;

View File

@ -1,18 +0,0 @@
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;

View File

@ -1,21 +0,0 @@
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;

View File

@ -3,7 +3,7 @@ const server = require("./api/server");
server.get("/", (req, res) => { server.get("/", (req, res) => {
res.json({ res.json({
message: `API server for Dijkstra. :)`, message: `API server for Dijkstra. :)`,
documentation: `Check out: https://git.ruihildt.xyz/ruihildt/dijkstra-backend` documentation: `Coming`
}); });
}); });

0
models/citiesModel.js Normal file
View File

View File

@ -1,4 +1,4 @@
const db = require('../../data/dbConfig'); const db = require('../data/dbConfig');
module.exports = { module.exports = {
getCountries, getCountries,

0
models/roadsModel.js Normal file
View File

977
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,18 +4,14 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"helmet": "^3.21.2",
"knex": "^0.20.8", "knex": "^0.20.8",
"nodejs": "^0.0.0", "nodejs": "^0.0.0",
"sqlite3": "^4.1.1" "sqlite3": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {},
"nodemon": "^2.0.2"
},
"scripts": { "scripts": {
"watch": "nodemon index.js", "test": "test",
"start": "node index.js" "start": "node index.js"
}, },
"author": "", "author": "",