fix: Restore GeoCoding from coordinates without API \o/
This commit is contained in:
parent
07a42f4d8d
commit
94d30121d4
@ -3,6 +3,7 @@ const debug = require('debug')('soundbirder:api');
|
||||
const debugLocale = require('debug')('soundbirder:locale');
|
||||
const debugResponses = require('debug')('soundbirder:api:responses');
|
||||
const quizzController = require('./quizz');
|
||||
const { getRegion } = require('./region');
|
||||
|
||||
const QUIZZ_SIZE = process.env.QUIZZ_SIZE ? process.env.QUIZZ_SIZE : 5;
|
||||
|
||||
@ -13,22 +14,32 @@ function getHome(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
async function region(req, res) {
|
||||
let {lat, lon} = req.query;
|
||||
lat = parseFloat(lat);
|
||||
lon = parseFloat(lon);
|
||||
const region = await getRegion(lat, lon);
|
||||
res.json({
|
||||
region
|
||||
})
|
||||
}
|
||||
|
||||
function quizz(req, res) {
|
||||
debug('Generating quizz');
|
||||
const { region } = req.query;
|
||||
// debug(`Coordinates: ${lat}, ${lng}`);
|
||||
const locale = req.i18n.locale;
|
||||
debugLocale("Locale:", locale);
|
||||
quizzController.generateQuizz(region, locale, QUIZZ_SIZE)
|
||||
quizzController.getQuizzCached(region, locale, QUIZZ_SIZE)
|
||||
.then(({ species, answer, audio }) => {
|
||||
req.session.answer = answer;
|
||||
res.json({ species, audio });
|
||||
res.json({ species, audio }).send();
|
||||
debug("Quizz sent");
|
||||
quizzController.cacheQuizz(); // Prepare the next question in advance.
|
||||
})
|
||||
.catch(error => {
|
||||
debug("Faced error while generating quizz");
|
||||
res.json({ error });
|
||||
throw error;
|
||||
debug(error)
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,5 +83,6 @@ const game = {
|
||||
|
||||
module.exports = {
|
||||
getHome,
|
||||
game
|
||||
game,
|
||||
region
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
const { json } = require('express');
|
||||
const { redisClient } = require('../redis');
|
||||
const debug = require('debug')('soundbirder:cache');
|
||||
|
||||
@ -6,6 +7,7 @@ function cacheResponse(request, response) {
|
||||
redisClient.set(request, JSON.stringify(response));
|
||||
}
|
||||
|
||||
|
||||
async function getCached(request) {
|
||||
const cached = await redisClient.get(request);
|
||||
if (cached) {
|
||||
@ -15,7 +17,22 @@ async function getCached(request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function push(key, value) {
|
||||
redisClient.lpush(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
async function pop(key) {
|
||||
const cached = await redisClient.lpop(key);
|
||||
debug("Pop cached", cached);
|
||||
if (cached) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cacheResponse,
|
||||
getCached
|
||||
getCached,
|
||||
push,
|
||||
pop
|
||||
}
|
@ -5,20 +5,49 @@ const cache = require('./cache');
|
||||
const eBird = require('@unclesamulus/ebird-api')(process.env.EBIRD_API_KEY);
|
||||
const XenoCanto = require('@unclesamulus/xeno-canto-api');
|
||||
const { choices, choice } = require('../utils/choices');
|
||||
// const { getRegion } = require('./region');
|
||||
const { getRegion } = require('./region');
|
||||
|
||||
function quizzKey(region, locale, size) {
|
||||
return `${region}:${locale}#${size}`;
|
||||
}
|
||||
|
||||
async function getQuizzCached(region, locale, size) {
|
||||
let key = quizzKey(region, locale, size);
|
||||
let quizz = await cache.pop(key);
|
||||
if (!quizz) {
|
||||
quizz = generateQuizz(region, locale, size);
|
||||
cache.push(key, quizz);
|
||||
}
|
||||
return quizz;
|
||||
}
|
||||
|
||||
async function cacheQuizz(region, locale, size) {
|
||||
let quizz = generateQuizz(region, locale, size);
|
||||
let key = quizzKey(region, locale, size);
|
||||
cache.push(key, quizz);
|
||||
}
|
||||
|
||||
async function generateQuizz(region, locale, size) {
|
||||
const quizz = {}
|
||||
try {
|
||||
const speciesSelection = await getSpeciesSelection(region, size);
|
||||
let speciesSelection = [];
|
||||
do {
|
||||
speciesSelection = await getSpeciesSelection(region, size);
|
||||
} while (speciesSelection == []);
|
||||
debugResponses(`Species selection: ${speciesSelection}`);
|
||||
const speciesSelectionLocalized = await getLocalizedNames(speciesSelection, locale);
|
||||
if (!speciesSelectionLocalized) {
|
||||
throw 'Not localized';
|
||||
}
|
||||
debugResponses('Localized species selection:', speciesSelectionLocalized);
|
||||
quizz.species = speciesSelectionLocalized;
|
||||
debug("Got species selection", quizz.species);
|
||||
let answer;
|
||||
do {
|
||||
answer = choice(speciesSelectionLocalized);
|
||||
if (answer === undefined) {
|
||||
debug('Species', speciesSelectionLocalized);
|
||||
}
|
||||
quizz.answer = answer;
|
||||
quizz.audio = await getAudio(answer.sciName);
|
||||
if (quizz.audio === undefined) {
|
||||
@ -99,6 +128,7 @@ async function getSpeciesList(regionCode) {
|
||||
} else {
|
||||
return eBird.product.spplist.in(regionCode)()
|
||||
.then(species => {
|
||||
species = species.filter((sp) => !sp.includes(" x "));
|
||||
cache.cacheResponse(`spplist-${regionCode}`, species);
|
||||
return species;
|
||||
})
|
||||
@ -124,5 +154,7 @@ function getAudio(speciesScientificName) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateQuizz
|
||||
generateQuizz,
|
||||
getQuizzCached,
|
||||
cacheQuizz
|
||||
}
|
@ -1,46 +1,41 @@
|
||||
require('dotenv').config();
|
||||
const debug = require('debug')('soundbirder:api:region');
|
||||
const cache = require('./cache');
|
||||
const axios = require('axios');
|
||||
require("dotenv").config();
|
||||
const debug = require("debug")("soundbirder:api:region");
|
||||
const cache = require("./cache");
|
||||
const axios = require("axios");
|
||||
|
||||
const OPENCAGE_API_KEY = process.env.OPENCAGE_API_KEY;
|
||||
const OPENCAGE_API_URL = 'https://api.opencagedata.com/geocode/v1/json?q=<lat>+<lon>&key=<key>';
|
||||
const { lookUp } = require("geojson-places");
|
||||
|
||||
async function getRegion(lat, lon) {
|
||||
const url = OPENCAGE_API_URL
|
||||
.replace('<lat>', lat)
|
||||
.replace('<lon>', lon)
|
||||
.replace('<key>', OPENCAGE_API_KEY);
|
||||
try {
|
||||
const cached = await cache.getCached(`region-${lat}-${lon}`);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
// Reverse geocoding to get the region info of Valladolid (Spain)
|
||||
const key = `region-${lat}-${lon}`;
|
||||
try {
|
||||
const cached = await cache.getCached(key);
|
||||
if (cached) {
|
||||
const { region } = cached;
|
||||
return region;
|
||||
}
|
||||
return axios.get(url).then(response => {
|
||||
const { results } = response.data;
|
||||
const region = results[0].components;
|
||||
region.country_code = region.country_code.toUpperCase();
|
||||
cache.cacheResponse(url, region);
|
||||
return { country_code, country, state, county } = region;
|
||||
}).catch(error => {
|
||||
throw error;
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
const result = lookUp(lat, lon);
|
||||
const region = result.country_a2;
|
||||
cache.cacheResponse(key, {region});
|
||||
return region;
|
||||
}
|
||||
|
||||
function getRegionCode({ country_code, country, state, county }) {
|
||||
return eBird.ref.region.list('subnational1', country_code)()
|
||||
.then(states => {
|
||||
console.log(states);
|
||||
const regionCode = states.find(region => region.name === state).code;
|
||||
return regionCode;
|
||||
}).catch(error => {
|
||||
throw error;
|
||||
});
|
||||
return eBird.ref.region
|
||||
.list("subnational1", country_code)()
|
||||
.then((states) => {
|
||||
console.log(states);
|
||||
const regionCode = states.find((region) => region.name === state).code;
|
||||
return regionCode;
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getRegion,
|
||||
}
|
||||
getRegion,
|
||||
};
|
||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@unclesamulus/xeno-canto-api": "^0.0.0",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"axios": "^0.27.2",
|
||||
"codegrid-js": "github:hlaw/codegrid-js",
|
||||
"connect-redis": "^6.1.3",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"csurf": "^1.11.0",
|
||||
@ -20,6 +21,7 @@
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.17.3",
|
||||
"feather-icons": "^4.29.0",
|
||||
"geojson-places": "^1.0.8",
|
||||
"http-errors": "~1.6.3",
|
||||
"i18n-2": "^0.7.3",
|
||||
"leaflet": "^1.8.0",
|
||||
@ -610,6 +612,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/codegrid-js": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "git+ssh://git@github.com/hlaw/codegrid-js.git#38decfe10dced9006c6b722cc403d7c1217abe98",
|
||||
"license": "WTFPL"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -1264,6 +1271,14 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/geojson-places": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/geojson-places/-/geojson-places-1.0.8.tgz",
|
||||
"integrity": "sha512-4jqzFgpz9S3QZ9KsDTvokuf3qCygfhgYlBaVHhGt27bI9uzvbxacFz19qxGui1WUAwYXj39IZdtATsQ1ggtlOQ==",
|
||||
"dependencies": {
|
||||
"just-clone": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
@ -1527,6 +1542,11 @@
|
||||
"promise": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/just-clone": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz",
|
||||
"integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA=="
|
||||
},
|
||||
"node_modules/leaflet": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||
|
@ -11,6 +11,7 @@
|
||||
"@unclesamulus/xeno-canto-api": "^0.0.0",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"axios": "^0.27.2",
|
||||
"codegrid-js": "github:hlaw/codegrid-js",
|
||||
"connect-redis": "^6.1.3",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"csurf": "^1.11.0",
|
||||
@ -19,6 +20,7 @@
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.17.3",
|
||||
"feather-icons": "^4.29.0",
|
||||
"geojson-places": "^1.0.8",
|
||||
"http-errors": "~1.6.3",
|
||||
"i18n-2": "^0.7.3",
|
||||
"leaflet": "^1.8.0",
|
||||
|
@ -20,27 +20,23 @@ async function query(endpoint, params) {
|
||||
return await get(`${API_URL}/${endpoint}`, params);
|
||||
}
|
||||
|
||||
function getQuizz(REGION) {
|
||||
return query('game/quizz', { region: REGION })
|
||||
.then(data => {
|
||||
return data;
|
||||
}).catch(error => {
|
||||
throw error;
|
||||
});
|
||||
function getQuizz(region) {
|
||||
return query('game/quizz', { region })
|
||||
}
|
||||
|
||||
function checkAnswer(speciesId) {
|
||||
return query(`game/check`, {
|
||||
species: speciesId
|
||||
}).then(data => {
|
||||
return data;
|
||||
}).catch(error => {
|
||||
throw error;
|
||||
});
|
||||
function getRegion(lat, lon) {
|
||||
return query('region', { lat, lon })
|
||||
}
|
||||
|
||||
function checkAnswer(species) {
|
||||
return query('game/check', {
|
||||
species: species
|
||||
})
|
||||
}
|
||||
|
||||
const client = {
|
||||
getQuizz,
|
||||
getRegion,
|
||||
checkAnswer
|
||||
}
|
||||
|
||||
|
@ -19,24 +19,28 @@ function geolocationStep() {
|
||||
if (map != undefined)
|
||||
geolocationHandler();
|
||||
if (startButton != undefined)
|
||||
startButton.addEventListener('click', quizzStep);
|
||||
startButton.addEventListener('click', regionCoder);
|
||||
}
|
||||
|
||||
const REGION = "FR";
|
||||
|
||||
function quizzStep() {
|
||||
function regionCoder() {
|
||||
// Start by disallowing geolocation step
|
||||
gameMapStep.classList.add('none');
|
||||
// Then allow the quizz step
|
||||
gameQuizzStep.classList.remove('none');
|
||||
|
||||
// Retrieve coordinates from former done geolocation (TODO: fix the need of cookie)
|
||||
// const coordinates = getCoordinates();
|
||||
// const [lat, lng] = coordinates;
|
||||
// console.log("Coordinates: " + `${lat}, ${lng}`);
|
||||
client.getQuizz(REGION)
|
||||
const coordinates = getCoordinates();
|
||||
let [lat, lon] = coordinates;
|
||||
client.getRegion(lat, lon).then(data => {
|
||||
let { region } = data;
|
||||
console.log(region);
|
||||
quizzStep(region);
|
||||
});
|
||||
}
|
||||
|
||||
function quizzStep(region) {
|
||||
client.getQuizz(region)
|
||||
.then(quizz => {
|
||||
// Display the quizz
|
||||
displayQuizz(quizz);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
|
@ -11,4 +11,7 @@ router.route('/game/quizz')
|
||||
router.route('/game/check')
|
||||
.get(apiController.game.check);
|
||||
|
||||
router.route('/region')
|
||||
.get(apiController.region);
|
||||
|
||||
module.exports = router;
|
@ -1,7 +1,7 @@
|
||||
.game
|
||||
.game-map-step
|
||||
//- #map.h-100
|
||||
//- button.button.geolocation-button
|
||||
#map.h-100
|
||||
button.button.geolocation-button
|
||||
i(data-feather="map-pin")
|
||||
button.button.start-button
|
||||
i(data-feather="play")
|
||||
|
Loading…
Reference in New Issue
Block a user