This commit is contained in:
parent
d721f2876e
commit
30db53bd95
38
apps/blakus-api/migrations/20250315152617_categories.js
Normal file
38
apps/blakus-api/migrations/20250315152617_categories.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema
|
||||||
|
.createTable('categories', function (table) {
|
||||||
|
table.uuid('id').primary().defaultTo(knex.raw(`gen_random_uuid()`));
|
||||||
|
table.string('name').notNullable();
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.alterTable('places', function(table) {
|
||||||
|
table.uuid('category_id').references('id').inTable('categories')
|
||||||
|
})
|
||||||
|
.createTable('subcategories', function(table) {
|
||||||
|
table.uuid('id').primary().defaultTo(knex.raw(`gen_random_uuid()`));
|
||||||
|
table.string('name').notNullable();
|
||||||
|
table.uuid('category_id').references('id').inTable('categories').onDelete('CASCADE');
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.createTable('place_subcategories', function(table) {
|
||||||
|
table.uuid('place_id').references('id').inTable('places').onDelete('CASCADE');
|
||||||
|
table.uuid('subcategory_id').references('id').inTable('subcategories').onDelete('CASCADE');
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.down = function(knex) {
|
||||||
|
knex.schema.alterTable('places', function(table) {
|
||||||
|
table.dropColumn('category_id');
|
||||||
|
});
|
||||||
|
knex.schema.dropTable('categories');
|
||||||
|
knex.schema.dropTable('subcategories');
|
||||||
|
knex.schema.dropTable('place_subcategories');
|
||||||
|
};
|
||||||
@ -74,7 +74,7 @@
|
|||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "apps/blakus-api",
|
"cwd": "apps/blakus-api",
|
||||||
"command": "npx knex migrate:make farts"
|
"command": "npx knex migrate:make new"
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"name": {
|
"name": {
|
||||||
|
|||||||
10
apps/blakus-api/seeds/categories.js
Normal file
10
apps/blakus-api/seeds/categories.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const CATEGORIES = require('./categories.json');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.seed = async function(knex) {
|
||||||
|
await knex('categories').del()
|
||||||
|
await knex('categories').insert(CATEGORIES.map(({name}) => ({name})));
|
||||||
|
};
|
||||||
10
apps/blakus-api/seeds/categories.json
Normal file
10
apps/blakus-api/seeds/categories.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
{ "name": "restaurant", "subcategories": [] },
|
||||||
|
{ "name": "cafe", "subcategories": [] },
|
||||||
|
{ "name": "bar", "subcategories": [] },
|
||||||
|
{ "name": "pub", "subcategories": [] },
|
||||||
|
{ "name": "biergarten", "subcategories": [] },
|
||||||
|
{ "name": "fast_food", "subcategories": [] },
|
||||||
|
{ "name": "food_court", "subcategories": [] },
|
||||||
|
{ "name": "ice_cream", "subcategories": [] }
|
||||||
|
]
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const { fakerLV : faker } = require('@faker-js/faker');
|
const { fakerLV : faker } = require('@faker-js/faker');
|
||||||
|
// const CATEGORIES = require('./categories.json');
|
||||||
const PLACE_COUNT = 1000;
|
const PLACE_COUNT = 1000;
|
||||||
const BBOX_RIGA = [23.755875,56.815914,24.466553,57.067617];
|
const BBOX_RIGA = [23.755875,56.815914,24.466553,57.067617];
|
||||||
const [ BBOX_MIN_LAT, BBOX_MIN_LON, BBOX_MAX_LAT, BBOX_MAX_LON ] = BBOX_RIGA;
|
const [ BBOX_MIN_LAT, BBOX_MIN_LON, BBOX_MAX_LAT, BBOX_MAX_LON ] = BBOX_RIGA;
|
||||||
@ -10,6 +10,8 @@ const [ BBOX_MIN_LAT, BBOX_MIN_LON, BBOX_MAX_LAT, BBOX_MAX_LON ] = BBOX_RIGA;
|
|||||||
*/
|
*/
|
||||||
exports.seed = async function(knex) {
|
exports.seed = async function(knex) {
|
||||||
|
|
||||||
|
const categories = await knex('categories').select('id');
|
||||||
|
|
||||||
const createPlace = () => {
|
const createPlace = () => {
|
||||||
const coords = [
|
const coords = [
|
||||||
faker.location.latitude({ min: BBOX_MIN_LAT, max: BBOX_MAX_LAT }),
|
faker.location.latitude({ min: BBOX_MIN_LAT, max: BBOX_MAX_LAT }),
|
||||||
@ -26,12 +28,13 @@ exports.seed = async function(knex) {
|
|||||||
street: faker.location.street(),
|
street: faker.location.street(),
|
||||||
house: faker.helpers.maybe(() => faker.person.lastName(), { probability: 0.1 }) || faker.location.buildingNumber(),
|
house: faker.helpers.maybe(() => faker.person.lastName(), { probability: 0.1 }) || faker.location.buildingNumber(),
|
||||||
postalCode: faker.location.zipCode(),
|
postalCode: faker.location.zipCode(),
|
||||||
|
category_id: faker.helpers.arrayElement(categories.map(c => c.id)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const places = new Array(PLACE_COUNT).fill(() => createPlace()).map((fn, i) => fn(i));
|
||||||
|
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
await knex('places').del()
|
await knex('places').del()
|
||||||
await knex('places').insert([
|
await knex('places').insert(places);
|
||||||
...new Array(PLACE_COUNT).fill(() => createPlace()).map((fn, i) => fn(i)),
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export default async function (fastify: FastifyInstance) {
|
|||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string' },
|
id: { type: 'string' },
|
||||||
title: { type: 'string' },
|
title: { type: 'string' },
|
||||||
amenity: { type: 'string' },
|
category: { type: 'string' },
|
||||||
address: {
|
address: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@ -51,23 +51,26 @@ export default async function (fastify: FastifyInstance) {
|
|||||||
}, async function (request, reply) {
|
}, async function (request, reply) {
|
||||||
const {lat, lon} = request.query;
|
const {lat, lon} = request.query;
|
||||||
|
|
||||||
const knownPlaces = await db.select(
|
const knownPlaces = await db('places')
|
||||||
'id',
|
.leftJoin('categories', 'places.category_id', 'categories.id')
|
||||||
'name',
|
.select(
|
||||||
'externalId',
|
'places.id',
|
||||||
'country',
|
'places.name',
|
||||||
'county',
|
'places.externalId',
|
||||||
'district',
|
'places.country',
|
||||||
'city',
|
'places.county',
|
||||||
'street',
|
'places.district',
|
||||||
'house',
|
'places.city',
|
||||||
'postalCode',
|
'places.street',
|
||||||
db.raw('ST_Y(coordinates::geometry) AS lat'),
|
'places.house',
|
||||||
db.raw('ST_X(coordinates::geometry) AS lon')
|
'places.postalCode',
|
||||||
|
db.raw('ST_Y(places.coordinates::geometry) AS lat'),
|
||||||
|
db.raw('ST_X(places.coordinates::geometry) AS lon'),
|
||||||
|
'categories.name AS category'
|
||||||
).whereRaw(
|
).whereRaw(
|
||||||
'ST_DWithin(coordinates, ST_SetSRID(ST_MakePoint(?, ?), 4326), ?)',
|
'ST_DWithin(places.coordinates, ST_SetSRID(ST_MakePoint(?, ?), 4326), ?)',
|
||||||
[lon, lat, 2000]
|
[lon, lat, 2000]
|
||||||
).from('places');
|
);
|
||||||
|
|
||||||
// const externalIds = await db.select('externalId').from('places').where('externalId', 'like', 'osm:%');
|
// const externalIds = await db.select('externalId').from('places').where('externalId', 'like', 'osm:%');
|
||||||
|
|
||||||
@ -97,7 +100,7 @@ export default async function (fastify: FastifyInstance) {
|
|||||||
const blakus = knownPlaces.map(place => ({
|
const blakus = knownPlaces.map(place => ({
|
||||||
id: place.id,
|
id: place.id,
|
||||||
title: place.name,
|
title: place.name,
|
||||||
amenity: 'pub',
|
category: place.category,
|
||||||
address: {
|
address: {
|
||||||
country: place.country,
|
country: place.country,
|
||||||
county: place.county,
|
county: place.county,
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="_amenity_restaurant_CYImm">"Restaurant"</string>
|
<string name="_category_restaurant_ZNNv4O">"Restaurant"</string>
|
||||||
<string name="_amenity_pub_hVjSx">"Pub"</string>
|
<string name="_category_pub_uGSaR">"Pub"</string>
|
||||||
<string name="_amenity_ice_cream_12K4Gu">"Ice Cream"</string>
|
<string name="_category_ice_cream_Z1ic9A2">"Ice Cream"</string>
|
||||||
<string name="_amenity_food_court_Z2qUk6P">"Food Court"</string>
|
<string name="_category_food_court_1bszfU">"Food Court"</string>
|
||||||
<string name="_amenity_fast_food_Z1zDs8J">"Fast Food"</string>
|
<string name="_category_fast_food_19ArnF">"Fast Food"</string>
|
||||||
<string name="_amenity_cafe_Z13hX6o">"Cafe"</string>
|
<string name="_category_cafe_laiRP">"Cafe"</string>
|
||||||
<string name="_amenity_biergarten_zXiC4">"Beer Garden"</string>
|
<string name="_category_biergarten_ZQOUO7">"Beer Garden"</string>
|
||||||
<string name="_amenity_bar_hVg75">"Bar"</string>
|
<string name="_category_bar_uGOop">"Bar"</string>
|
||||||
<string name="_app_name_1k3Sbz">"Blakus"</string>
|
<string name="_app_name_1k3Sbz">"Blakus"</string>
|
||||||
<string name="app_name">"Blakus"</string>
|
<string name="app_name">"Blakus"</string>
|
||||||
<string name="title_activity_kimera">"Blakus"</string>
|
<string name="title_activity_kimera">"Blakus"</string>
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="_amenity_restaurant_CYImm">"Restorāns"</string>
|
<string name="_category_restaurant_ZNNv4O">"Restorāns"</string>
|
||||||
<string name="_amenity_pub_hVjSx">"Krogs"</string>
|
<string name="_category_pub_uGSaR">"Krogs"</string>
|
||||||
<string name="_amenity_ice_cream_12K4Gu">"Saldējums"</string>
|
<string name="_category_ice_cream_Z1ic9A2">"Saldējums"</string>
|
||||||
<string name="_amenity_food_court_Z2qUk6P">"Ēdināšanas laukums"</string>
|
<string name="_category_food_court_1bszfU">"Ēdināšanas laukums"</string>
|
||||||
<string name="_amenity_fast_food_Z1zDs8J">"Ātras uzkodas"</string>
|
<string name="_category_fast_food_19ArnF">"Ātras uzkodas"</string>
|
||||||
<string name="_amenity_cafe_Z13hX6o">"Kafejnīca"</string>
|
<string name="_category_cafe_laiRP">"Kafejnīca"</string>
|
||||||
<string name="_amenity_biergarten_zXiC4">"Biergarten"</string>
|
<string name="_category_biergarten_ZQOUO7">"Biergarten"</string>
|
||||||
<string name="_amenity_bar_hVg75">"Bārs"</string>
|
<string name="_category_bar_uGOop">"Bārs"</string>
|
||||||
<string name="_app_name_1k3Sbz">"Blakus"</string>
|
<string name="_app_name_1k3Sbz">"Blakus"</string>
|
||||||
<string name="app_name">"Blakus"</string>
|
<string name="app_name">"Blakus"</string>
|
||||||
<string name="title_activity_kimera">"Blakus"</string>
|
<string name="title_activity_kimera">"Blakus"</string>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app.name": "Blakus",
|
"app.name": "Blakus",
|
||||||
"amenity": {
|
"category": {
|
||||||
"bar": "Bar",
|
"bar": "Bar",
|
||||||
"biergarten": "Beer Garden",
|
"biergarten": "Beer Garden",
|
||||||
"cafe": "Cafe",
|
"cafe": "Cafe",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app.name": "Blakus",
|
"app.name": "Blakus",
|
||||||
"amenity": {
|
"category": {
|
||||||
"bar": "Bārs",
|
"bar": "Bārs",
|
||||||
"biergarten": "Biergarten",
|
"biergarten": "Biergarten",
|
||||||
"cafe": "Kafejnīca",
|
"cafe": "Kafejnīca",
|
||||||
|
|||||||
@ -25,7 +25,7 @@ type ReverseGeocodeResult = {
|
|||||||
type BlakusItem = {
|
type BlakusItem = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
amenity: string;
|
category: string;
|
||||||
tags: Record<string, string>;
|
tags: Record<string, string>;
|
||||||
affinity: number;
|
affinity: number;
|
||||||
coords: {
|
coords: {
|
||||||
@ -88,7 +88,7 @@ export class HelloWorldModel extends Observable {
|
|||||||
|
|
||||||
this.items = await this.getBlakus(params);
|
this.items = await this.getBlakus(params);
|
||||||
|
|
||||||
this.items.forEach(({ coords, title, amenity, affinity, id }) => {
|
this.items.forEach(({ coords, title, category, affinity, id }) => {
|
||||||
this.#map.addMarker({
|
this.#map.addMarker({
|
||||||
id,
|
id,
|
||||||
position: {
|
position: {
|
||||||
@ -97,7 +97,7 @@ export class HelloWorldModel extends Observable {
|
|||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
title,
|
title,
|
||||||
description: localize(`amenity.${amenity}`),
|
description: localize(`category.${category}`),
|
||||||
affinity
|
affinity
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user