houses: Leefbaarometer liveability per listing (PDOK geocode + LBM 2024 grid/buurt), min-score filter and sort; store every regional listing (no price caps at scrape time)
All checks were successful
Deploy production / deploy (push) Successful in 2m33s

This commit is contained in:
2026-10-24 15:40:20 +00:00
parent e95b7da1e3
commit 113eb54cd9
13 changed files with 19150 additions and 13 deletions

View File

@@ -14,6 +14,7 @@ import type { AnyPgColumn } from 'drizzle-orm/pg-core';
import { houseSchema } from './schemas';
import { sql } from 'drizzle-orm';
import { id, timestamptz } from './helpers';
import type { Liveability } from '../../../house-tracker/liveability';
export const houseListings = houseSchema.table(
'listings',
@@ -66,7 +67,23 @@ export const houseListings = houseSchema.table(
onDelete: 'set null'
}),
// Monthly rent including service costs, when a source shows it separately.
totalPriceEur: integer('total_price_eur')
totalPriceEur: integer('total_price_eur'),
// Leefbaarometer liveability (src/lib/house-tracker/liveability.ts). lbm_class is the
// 1-9 class (zeer onvoldoende .. uitstekend) of the 100 m grid cell, or of the buurt when
// there is no cell or only a street-level location; lbm_onv_class the 1-9 class of the
// "overlast & onveiligheid" dimension. lbm_query is the address text that was geocoded,
// so a changed address is re-scored.
lbmScore: doublePrecision('lbm_score'),
lbmClass: integer('lbm_class'),
lbmOnvClass: integer('lbm_onv_class'),
lbmLevel: text('lbm_level'),
lbmPrecision: text('lbm_precision'),
lbmBuurtCode: text('lbm_buurt_code'),
lbmBuurtName: text('lbm_buurt_name'),
lbmYear: integer('lbm_year'),
lbmDetail: jsonb('lbm_detail').$type<Liveability>(),
lbmQuery: text('lbm_query'),
lbmScoredAt: timestamptz('lbm_scored_at')
},
(table) => [
check('listings_offer_check', sql`offer IN ('rent', 'buy')`),