feat(22begh): schema extension — flats+6 / objects+30 / developers+4 / metro_top3 #306
2 changed files with 377 additions and 8 deletions
|
|
@ -186,15 +186,25 @@ def _extract_list(payload: dict[str, Any]) -> list[dict[str, Any]]:
|
||||||
def _norm_object(row: dict[str, Any], region_cd: int | None = None) -> dict[str, Any]:
|
def _norm_object(row: dict[str, Any], region_cd: int | None = None) -> dict[str, Any]:
|
||||||
"""Map naш.дом.рф /kn/object row → domrf_kn_objects column dict.
|
"""Map naш.дом.рф /kn/object row → domrf_kn_objects column dict.
|
||||||
|
|
||||||
Real API field names:
|
Real API field names (confirmed via payload audit 2026-05-17, obj=65136):
|
||||||
objId, hobjId, developer{devId, shortName, fullName, groupName, companyGroup, devInn},
|
objId, hobjId, developer{devId, shortName, fullName, groupName, companyGroup, devInn},
|
||||||
rpdRegionCd, objAddr, shortAddr, objCommercNm, objFloorMin, objFloorMax,
|
rpdRegionCd, objAddr, shortAddr, objCommercNm, objFloorMin, objFloorMax,
|
||||||
objElemLivingCnt, objSquareLiving, objReady100PercDt, objClass, latitude, longitude,
|
objElemLivingCnt, objSquareLiving, objReady100PercDt, objClass, latitude, longitude,
|
||||||
objProblemFlg, problemFlag, siteStatus, objGreenHouseFlg, objGuarantyEscrowFlg,
|
objProblemFlg, problemFlag, siteStatus, objGreenHouseFlg, objGuarantyEscrowFlg,
|
||||||
objStatus, aiDescription, freeFlatsInfo{priceMin, numberFlats}.
|
objStatus, aiDescription, freeFlatsInfo{priceMin, numberFlats},
|
||||||
|
residentialBuildings (section_count), rpdNum (project_declaration_num),
|
||||||
|
objPublDt (project_published_at), metro{name, time, line, color, colors, isWalk, id}.
|
||||||
|
|
||||||
Note: DOM.РФ /kn/object list endpoint never populates `objClass` in API responses
|
Note: DOM.РФ /kn/object list endpoint never populates `objClass` in API responses
|
||||||
(field absent from payload). Class is extracted from `aiDescription` text as fallback.
|
(field absent from payload). Class is extracted from `aiDescription` text as fallback.
|
||||||
|
|
||||||
|
22e fields NOT in kn-API list payload (remain NULL until catalog scraper 22d):
|
||||||
|
first_floor_type, elevators_passenger/cargo_count, parking_total_slots,
|
||||||
|
guest_parking_inside/outside_count, ceiling_height_m, finishing_variants_count,
|
||||||
|
has_free_planning, playground_kids/sport_count, has_bike_paths, trash_areas_count,
|
||||||
|
has_ramp, has_low_platforms, has_wheelchair_lift, flat_area_min/max,
|
||||||
|
price_max_rub, price_per_m2_min/max, parking_provision_pct, avg_flat_area_m2,
|
||||||
|
domrf_score_location/transport/infrastructure.
|
||||||
"""
|
"""
|
||||||
dev = row.get("developer") if isinstance(row.get("developer"), dict) else {}
|
dev = row.get("developer") if isinstance(row.get("developer"), dict) else {}
|
||||||
company_group = _g(dev, "companyGroup") if dev else None
|
company_group = _g(dev, "companyGroup") if dev else None
|
||||||
|
|
@ -210,6 +220,34 @@ def _norm_object(row: dict[str, Any], region_cd: int | None = None) -> dict[str,
|
||||||
obj_id,
|
obj_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Metro: kn-API returns a single metro object (nearest station).
|
||||||
|
# Stored as metro_top3 JSON array (single element) for future multi-station extension.
|
||||||
|
metro_raw = row.get("metro")
|
||||||
|
metro_nearest_name: str | None = None
|
||||||
|
metro_nearest_walk_minutes: int | None = None
|
||||||
|
metro_top3: list[dict[str, Any]] | None = None
|
||||||
|
if isinstance(metro_raw, dict) and metro_raw.get("name"):
|
||||||
|
metro_nearest_name = metro_raw.get("name")
|
||||||
|
raw_time = metro_raw.get("time")
|
||||||
|
if raw_time is not None:
|
||||||
|
try:
|
||||||
|
metro_nearest_walk_minutes = round(float(raw_time))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
metro_top3 = [
|
||||||
|
{
|
||||||
|
"name": metro_raw.get("name"),
|
||||||
|
"time": raw_time,
|
||||||
|
"line": metro_raw.get("line"),
|
||||||
|
"color": metro_raw.get("color"),
|
||||||
|
"isWalk": metro_raw.get("isWalk"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# freeFlatsInfo.priceMin — price of cheapest available flat in the object
|
||||||
|
free_info = row.get("freeFlatsInfo") if isinstance(row.get("freeFlatsInfo"), dict) else {}
|
||||||
|
price_min_rub = _g(free_info, "priceMin") if free_info else None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"obj_id": _g(row, "objId", "obj_id", "id"),
|
"obj_id": _g(row, "objId", "obj_id", "id"),
|
||||||
"hobj_id": _g(row, "hobjId", "hobj_id"),
|
"hobj_id": _g(row, "hobjId", "hobj_id"),
|
||||||
|
|
@ -235,6 +273,43 @@ def _norm_object(row: dict[str, Any], region_cd: int | None = None) -> dict[str,
|
||||||
"latitude": _g(row, "latitude"),
|
"latitude": _g(row, "latitude"),
|
||||||
"longitude": _g(row, "longitude"),
|
"longitude": _g(row, "longitude"),
|
||||||
"obj_status": _g(row, "objStatus"),
|
"obj_status": _g(row, "objStatus"),
|
||||||
|
# 22e: fields mappable from kn-API list payload
|
||||||
|
"section_count": _g(row, "residentialBuildings"),
|
||||||
|
"project_declaration_num": _g(row, "rpdNum"),
|
||||||
|
"project_published_at": _to_date(_g(row, "objPublDt")),
|
||||||
|
"price_min_rub": price_min_rub,
|
||||||
|
"dev_group_name": _g(dev, "groupName"),
|
||||||
|
# 22e + 22h: metro (nearest station from kn-API, single object)
|
||||||
|
"metro_nearest_name": metro_nearest_name,
|
||||||
|
"metro_nearest_walk_minutes": metro_nearest_walk_minutes,
|
||||||
|
"metro_top3": json.dumps(metro_top3, ensure_ascii=False) if metro_top3 else None,
|
||||||
|
# 22e: fields NOT in kn-API list payload — filled by catalog scraper (22d)
|
||||||
|
"first_floor_type": None,
|
||||||
|
"elevators_passenger_count": None,
|
||||||
|
"elevators_cargo_count": None,
|
||||||
|
"parking_total_slots": None,
|
||||||
|
"guest_parking_inside_count": None,
|
||||||
|
"guest_parking_outside_count": None,
|
||||||
|
"ceiling_height_m": None,
|
||||||
|
"finishing_variants_count": None,
|
||||||
|
"has_free_planning": None,
|
||||||
|
"avg_flat_area_m2": None,
|
||||||
|
"playground_kids_count": None,
|
||||||
|
"playground_sport_count": None,
|
||||||
|
"has_bike_paths": None,
|
||||||
|
"trash_areas_count": None,
|
||||||
|
"has_ramp": None,
|
||||||
|
"has_low_platforms": None,
|
||||||
|
"has_wheelchair_lift": None,
|
||||||
|
"flat_area_min": None,
|
||||||
|
"flat_area_max": None,
|
||||||
|
"price_max_rub": None,
|
||||||
|
"price_per_m2_min": None,
|
||||||
|
"price_per_m2_max": None,
|
||||||
|
"parking_provision_pct": None,
|
||||||
|
"domrf_score_location": None,
|
||||||
|
"domrf_score_transport": None,
|
||||||
|
"domrf_score_infrastructure": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -308,6 +383,13 @@ def _norm_flat(row: dict[str, Any], region_cd: int | None) -> dict[str, Any]:
|
||||||
"city": _g(row, "city"),
|
"city": _g(row, "city"),
|
||||||
"region_cd": region_cd,
|
"region_cd": region_cd,
|
||||||
"obj_name": _g(row, "objName") or _g(row.get("objInfo") or {}, "objCommercNm"),
|
"obj_name": _g(row, "objName") or _g(row.get("objInfo") or {}, "objCommercNm"),
|
||||||
|
# 22b: новые поля квартиры
|
||||||
|
"section_no": _g(row, "_entrance"), # entranceNumber injected by _flatten_table
|
||||||
|
"finishing_type": None, # заполнит catalog scraper 22d
|
||||||
|
"ceiling_height_m": None, # заполнит catalog scraper 22d
|
||||||
|
"key_handover_dt": None, # заполнит catalog scraper 22d
|
||||||
|
"catalog_updated_at": None, # заполнит catalog scraper 22d
|
||||||
|
"catalog_url_hash": None, # заполнит catalog scraper 22d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -321,14 +403,38 @@ UPSERT_OBJECT_SQL = text(
|
||||||
floor_min, floor_max, flat_count, square_living, ready_dt,
|
floor_min, floor_max, flat_count, square_living, ready_dt,
|
||||||
problem_flag, site_status, green_house, escrow,
|
problem_flag, site_status, green_house, escrow,
|
||||||
obj_class, wall_type, energy_eff,
|
obj_class, wall_type, energy_eff,
|
||||||
latitude, longitude, obj_status, snapshot_date
|
latitude, longitude, obj_status,
|
||||||
|
section_count, project_declaration_num, project_published_at,
|
||||||
|
price_min_rub, dev_group_name,
|
||||||
|
metro_nearest_name, metro_nearest_walk_minutes, metro_top3,
|
||||||
|
first_floor_type, elevators_passenger_count, elevators_cargo_count,
|
||||||
|
parking_total_slots, guest_parking_inside_count, guest_parking_outside_count,
|
||||||
|
ceiling_height_m, finishing_variants_count, has_free_planning, avg_flat_area_m2,
|
||||||
|
playground_kids_count, playground_sport_count, has_bike_paths, trash_areas_count,
|
||||||
|
has_ramp, has_low_platforms, has_wheelchair_lift,
|
||||||
|
flat_area_min, flat_area_max, price_max_rub, price_per_m2_min, price_per_m2_max,
|
||||||
|
parking_provision_pct, domrf_score_location, domrf_score_transport,
|
||||||
|
domrf_score_infrastructure,
|
||||||
|
snapshot_date
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:obj_id, :hobj_id, :comm_name, :addr, :short_addr, :region_cd,
|
:obj_id, :hobj_id, :comm_name, :addr, :short_addr, :region_cd,
|
||||||
:dev_id, :dev_name, :dev_inn,
|
:dev_id, :dev_name, :dev_inn,
|
||||||
:floor_min, :floor_max, :flat_count, :square_living, :ready_dt,
|
:floor_min, :floor_max, :flat_count, :square_living, :ready_dt,
|
||||||
:problem_flag, :site_status, :green_house, :escrow,
|
:problem_flag, :site_status, :green_house, :escrow,
|
||||||
:obj_class, :wall_type, :energy_eff,
|
:obj_class, :wall_type, :energy_eff,
|
||||||
:latitude, :longitude, :obj_status, :snapshot_date
|
:latitude, :longitude, :obj_status,
|
||||||
|
:section_count, :project_declaration_num, :project_published_at,
|
||||||
|
:price_min_rub, :dev_group_name,
|
||||||
|
:metro_nearest_name, :metro_nearest_walk_minutes, CAST(:metro_top3 AS jsonb),
|
||||||
|
:first_floor_type, :elevators_passenger_count, :elevators_cargo_count,
|
||||||
|
:parking_total_slots, :guest_parking_inside_count, :guest_parking_outside_count,
|
||||||
|
:ceiling_height_m, :finishing_variants_count, :has_free_planning, :avg_flat_area_m2,
|
||||||
|
:playground_kids_count, :playground_sport_count, :has_bike_paths, :trash_areas_count,
|
||||||
|
:has_ramp, :has_low_platforms, :has_wheelchair_lift,
|
||||||
|
:flat_area_min, :flat_area_max, :price_max_rub, :price_per_m2_min, :price_per_m2_max,
|
||||||
|
:parking_provision_pct, :domrf_score_location, :domrf_score_transport,
|
||||||
|
:domrf_score_infrastructure,
|
||||||
|
:snapshot_date
|
||||||
)
|
)
|
||||||
ON CONFLICT (obj_id, snapshot_date) DO UPDATE SET
|
ON CONFLICT (obj_id, snapshot_date) DO UPDATE SET
|
||||||
comm_name = EXCLUDED.comm_name,
|
comm_name = EXCLUDED.comm_name,
|
||||||
|
|
@ -345,7 +451,91 @@ UPSERT_OBJECT_SQL = text(
|
||||||
energy_eff = COALESCE(EXCLUDED.energy_eff, domrf_kn_objects.energy_eff),
|
energy_eff = COALESCE(EXCLUDED.energy_eff, domrf_kn_objects.energy_eff),
|
||||||
latitude = EXCLUDED.latitude,
|
latitude = EXCLUDED.latitude,
|
||||||
longitude = EXCLUDED.longitude,
|
longitude = EXCLUDED.longitude,
|
||||||
obj_status = EXCLUDED.obj_status
|
obj_status = EXCLUDED.obj_status,
|
||||||
|
section_count = COALESCE(EXCLUDED.section_count, domrf_kn_objects.section_count),
|
||||||
|
project_declaration_num = COALESCE(
|
||||||
|
EXCLUDED.project_declaration_num, domrf_kn_objects.project_declaration_num
|
||||||
|
),
|
||||||
|
project_published_at = COALESCE(
|
||||||
|
EXCLUDED.project_published_at, domrf_kn_objects.project_published_at
|
||||||
|
),
|
||||||
|
price_min_rub = EXCLUDED.price_min_rub,
|
||||||
|
dev_group_name = COALESCE(EXCLUDED.dev_group_name, domrf_kn_objects.dev_group_name),
|
||||||
|
metro_nearest_name = COALESCE(
|
||||||
|
EXCLUDED.metro_nearest_name, domrf_kn_objects.metro_nearest_name
|
||||||
|
),
|
||||||
|
metro_nearest_walk_minutes = COALESCE(
|
||||||
|
EXCLUDED.metro_nearest_walk_minutes, domrf_kn_objects.metro_nearest_walk_minutes
|
||||||
|
),
|
||||||
|
metro_top3 = COALESCE(EXCLUDED.metro_top3, domrf_kn_objects.metro_top3),
|
||||||
|
first_floor_type = COALESCE(
|
||||||
|
EXCLUDED.first_floor_type, domrf_kn_objects.first_floor_type
|
||||||
|
),
|
||||||
|
elevators_passenger_count = COALESCE(
|
||||||
|
EXCLUDED.elevators_passenger_count, domrf_kn_objects.elevators_passenger_count
|
||||||
|
),
|
||||||
|
elevators_cargo_count = COALESCE(
|
||||||
|
EXCLUDED.elevators_cargo_count, domrf_kn_objects.elevators_cargo_count
|
||||||
|
),
|
||||||
|
parking_total_slots = COALESCE(
|
||||||
|
EXCLUDED.parking_total_slots, domrf_kn_objects.parking_total_slots
|
||||||
|
),
|
||||||
|
guest_parking_inside_count = COALESCE(
|
||||||
|
EXCLUDED.guest_parking_inside_count, domrf_kn_objects.guest_parking_inside_count
|
||||||
|
),
|
||||||
|
guest_parking_outside_count = COALESCE(
|
||||||
|
EXCLUDED.guest_parking_outside_count, domrf_kn_objects.guest_parking_outside_count
|
||||||
|
),
|
||||||
|
ceiling_height_m = COALESCE(
|
||||||
|
EXCLUDED.ceiling_height_m, domrf_kn_objects.ceiling_height_m
|
||||||
|
),
|
||||||
|
finishing_variants_count = COALESCE(
|
||||||
|
EXCLUDED.finishing_variants_count, domrf_kn_objects.finishing_variants_count
|
||||||
|
),
|
||||||
|
has_free_planning = COALESCE(
|
||||||
|
EXCLUDED.has_free_planning, domrf_kn_objects.has_free_planning
|
||||||
|
),
|
||||||
|
avg_flat_area_m2 = COALESCE(
|
||||||
|
EXCLUDED.avg_flat_area_m2, domrf_kn_objects.avg_flat_area_m2
|
||||||
|
),
|
||||||
|
playground_kids_count = COALESCE(
|
||||||
|
EXCLUDED.playground_kids_count, domrf_kn_objects.playground_kids_count
|
||||||
|
),
|
||||||
|
playground_sport_count = COALESCE(
|
||||||
|
EXCLUDED.playground_sport_count, domrf_kn_objects.playground_sport_count
|
||||||
|
),
|
||||||
|
has_bike_paths = COALESCE(EXCLUDED.has_bike_paths, domrf_kn_objects.has_bike_paths),
|
||||||
|
trash_areas_count = COALESCE(
|
||||||
|
EXCLUDED.trash_areas_count, domrf_kn_objects.trash_areas_count
|
||||||
|
),
|
||||||
|
has_ramp = COALESCE(EXCLUDED.has_ramp, domrf_kn_objects.has_ramp),
|
||||||
|
has_low_platforms = COALESCE(
|
||||||
|
EXCLUDED.has_low_platforms, domrf_kn_objects.has_low_platforms
|
||||||
|
),
|
||||||
|
has_wheelchair_lift = COALESCE(
|
||||||
|
EXCLUDED.has_wheelchair_lift, domrf_kn_objects.has_wheelchair_lift
|
||||||
|
),
|
||||||
|
flat_area_min = COALESCE(EXCLUDED.flat_area_min, domrf_kn_objects.flat_area_min),
|
||||||
|
flat_area_max = COALESCE(EXCLUDED.flat_area_max, domrf_kn_objects.flat_area_max),
|
||||||
|
price_max_rub = COALESCE(EXCLUDED.price_max_rub, domrf_kn_objects.price_max_rub),
|
||||||
|
price_per_m2_min = COALESCE(
|
||||||
|
EXCLUDED.price_per_m2_min, domrf_kn_objects.price_per_m2_min
|
||||||
|
),
|
||||||
|
price_per_m2_max = COALESCE(
|
||||||
|
EXCLUDED.price_per_m2_max, domrf_kn_objects.price_per_m2_max
|
||||||
|
),
|
||||||
|
parking_provision_pct = COALESCE(
|
||||||
|
EXCLUDED.parking_provision_pct, domrf_kn_objects.parking_provision_pct
|
||||||
|
),
|
||||||
|
domrf_score_location = COALESCE(
|
||||||
|
EXCLUDED.domrf_score_location, domrf_kn_objects.domrf_score_location
|
||||||
|
),
|
||||||
|
domrf_score_transport = COALESCE(
|
||||||
|
EXCLUDED.domrf_score_transport, domrf_kn_objects.domrf_score_transport
|
||||||
|
),
|
||||||
|
domrf_score_infrastructure = COALESCE(
|
||||||
|
EXCLUDED.domrf_score_infrastructure, domrf_kn_objects.domrf_score_infrastructure
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -354,11 +544,17 @@ UPSERT_FLAT_SQL = text(
|
||||||
INSERT INTO domrf_kn_flats (
|
INSERT INTO domrf_kn_flats (
|
||||||
id, ods_id, flat_type, flat_number, is_studio, total_area, living_area,
|
id, ods_id, flat_type, flat_number, is_studio, total_area, living_area,
|
||||||
rooms, status, price_rub, price_per_m2, floor, num_floors, obj_id,
|
rooms, status, price_rub, price_per_m2, floor, num_floors, obj_id,
|
||||||
city, region_cd, obj_name, snapshot_date
|
city, region_cd, obj_name,
|
||||||
|
section_no, finishing_type, ceiling_height_m,
|
||||||
|
key_handover_dt, catalog_updated_at, catalog_url_hash,
|
||||||
|
snapshot_date
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:id, :ods_id, :flat_type, :flat_number, :is_studio, :total_area, :living_area,
|
:id, :ods_id, :flat_type, :flat_number, :is_studio, :total_area, :living_area,
|
||||||
:rooms, :status, :price_rub, :price_per_m2, :floor, :num_floors, :obj_id,
|
:rooms, :status, :price_rub, :price_per_m2, :floor, :num_floors, :obj_id,
|
||||||
:city, :region_cd, :obj_name, :snapshot_date
|
:city, :region_cd, :obj_name,
|
||||||
|
:section_no, :finishing_type, :ceiling_height_m,
|
||||||
|
:key_handover_dt, :catalog_updated_at, :catalog_url_hash,
|
||||||
|
:snapshot_date
|
||||||
)
|
)
|
||||||
ON CONFLICT (id, snapshot_date) DO UPDATE SET
|
ON CONFLICT (id, snapshot_date) DO UPDATE SET
|
||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
|
|
@ -373,7 +569,19 @@ UPSERT_FLAT_SQL = text(
|
||||||
total_area = COALESCE(EXCLUDED.total_area, domrf_kn_flats.total_area),
|
total_area = COALESCE(EXCLUDED.total_area, domrf_kn_flats.total_area),
|
||||||
rooms = COALESCE(EXCLUDED.rooms, domrf_kn_flats.rooms),
|
rooms = COALESCE(EXCLUDED.rooms, domrf_kn_flats.rooms),
|
||||||
floor = COALESCE(EXCLUDED.floor, domrf_kn_flats.floor),
|
floor = COALESCE(EXCLUDED.floor, domrf_kn_flats.floor),
|
||||||
num_floors = COALESCE(EXCLUDED.num_floors, domrf_kn_flats.num_floors)
|
num_floors = COALESCE(EXCLUDED.num_floors, domrf_kn_flats.num_floors),
|
||||||
|
section_no = COALESCE(EXCLUDED.section_no, domrf_kn_flats.section_no),
|
||||||
|
finishing_type = COALESCE(EXCLUDED.finishing_type, domrf_kn_flats.finishing_type),
|
||||||
|
ceiling_height_m = COALESCE(
|
||||||
|
EXCLUDED.ceiling_height_m, domrf_kn_flats.ceiling_height_m
|
||||||
|
),
|
||||||
|
key_handover_dt = COALESCE(EXCLUDED.key_handover_dt, domrf_kn_flats.key_handover_dt),
|
||||||
|
catalog_updated_at = COALESCE(
|
||||||
|
EXCLUDED.catalog_updated_at, domrf_kn_flats.catalog_updated_at
|
||||||
|
),
|
||||||
|
catalog_url_hash = COALESCE(
|
||||||
|
EXCLUDED.catalog_url_hash, domrf_kn_flats.catalog_url_hash
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
161
data/sql/113_22begh_kn_schema_extension.sql
Normal file
161
data/sql/113_22begh_kn_schema_extension.sql
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
-- Migration 112: schema extension for issues #297 sub-tasks 22b + 22e + 22g + 22h
|
||||||
|
-- Adds 6 flat-level cols, 30 object-level cols (incl. metro_top3), 4 developer metrics.
|
||||||
|
--
|
||||||
|
-- Payload audit (obj=65136, 2026-05-17):
|
||||||
|
-- kn_api payload keys: objId, hobjId, objCommercNm, objAddr, shortAddr, rpdRegionCd,
|
||||||
|
-- developer{devId,devInn,orgForm,fullName,groupName,shortName,companyGroup},
|
||||||
|
-- rpdNum, objPublDt, objStatus, siteStatus, objFloorMax, objFloorMin, objPriceAVG,
|
||||||
|
-- problemFlag, rpdRegionCd, objSquareLiving, objElemLivingCnt, objGreenHouseFlg,
|
||||||
|
-- objGuarantyEscrowFlg, objReady100PercDt, hobjRenderPhotoUrl, aiDescription,
|
||||||
|
-- freeFlatsInfo{priceMin, numberFlats}, residentialBuildings,
|
||||||
|
-- metro{id, line, name, time, color, colors, isWalk}, latitude, longitude, pdId, buildType.
|
||||||
|
--
|
||||||
|
-- flat-level (portal/table): flatId, odsId, elemId, type, number, isStudio,
|
||||||
|
-- totalArea, livingArea, rooms, status, price, pricePerSquareMeter, numberFloors,
|
||||||
|
-- _objId(injected), _floor(injected), _entrance(injected as entranceNumber).
|
||||||
|
--
|
||||||
|
-- Fields that map NOW (from existing kn-API payload):
|
||||||
|
-- 22b: section_no (from _entrance/entranceNumber injected by _flatten_table)
|
||||||
|
-- 22e: section_count (residentialBuildings), project_declaration_num (rpdNum),
|
||||||
|
-- project_published_at (objPublDt), metro_nearest_name + metro_nearest_walk_minutes
|
||||||
|
-- (metro.name + metro.time), metro_top3 (metro wrapped as single-element array),
|
||||||
|
-- dev_group_name (developer.groupName), price_min_rub (freeFlatsInfo.priceMin)
|
||||||
|
--
|
||||||
|
-- Fields remaining NULL until catalog scraper 22d:
|
||||||
|
-- finishing_type, ceiling_height_m (flat), key_handover_dt, catalog_updated_at,
|
||||||
|
-- catalog_url_hash, first_floor_type, elevators_passenger_count, elevators_cargo_count,
|
||||||
|
-- parking_total_slots, guest_parking_inside_count, guest_parking_outside_count,
|
||||||
|
-- ceiling_height_m (obj), finishing_variants_count, has_free_planning,
|
||||||
|
-- playground_kids_count, playground_sport_count, has_bike_paths, trash_areas_count,
|
||||||
|
-- has_ramp, has_low_platforms, has_wheelchair_lift, flat_area_min, flat_area_max,
|
||||||
|
-- price_max_rub, price_per_m2_min, price_per_m2_max, parking_provision_pct,
|
||||||
|
-- avg_flat_area_m2, domrf_score_location, domrf_score_transport,
|
||||||
|
-- domrf_score_infrastructure, houses_building, houses_building_delayed,
|
||||||
|
-- houses_delivered, houses_delivered_delayed.
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
-- 22b — domrf_kn_flats +6 columns
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
COMMENT ON TABLE domrf_kn_flats IS 'Per-flat catalogue from DOM.RF kn-API portal/table endpoint';
|
||||||
|
|
||||||
|
ALTER TABLE domrf_kn_flats
|
||||||
|
ADD COLUMN IF NOT EXISTS section_no int,
|
||||||
|
ADD COLUMN IF NOT EXISTS finishing_type text,
|
||||||
|
ADD COLUMN IF NOT EXISTS ceiling_height_m numeric(3,2),
|
||||||
|
ADD COLUMN IF NOT EXISTS key_handover_dt date,
|
||||||
|
ADD COLUMN IF NOT EXISTS catalog_updated_at date,
|
||||||
|
ADD COLUMN IF NOT EXISTS catalog_url_hash text;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.section_no IS '22b: подъезд из entrance.entranceNumber (portal/table)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.finishing_type IS '22b/22d: тип отделки — заполнит catalog scraper (22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.ceiling_height_m IS '22b/22d: высота потолков — заполнит catalog scraper (22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.key_handover_dt IS '22b/22d: дата выдачи ключей — заполнит catalog scraper (22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.catalog_updated_at IS '22b/22d: дата обновления инфо на catalog-странице (22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_flats.catalog_url_hash IS '22b/22d: hash-часть URL квартиры на каталоге dom.rf (22d)';
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
-- 22e + 22h — domrf_kn_objects +30 columns (incl. metro_top3)
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
COMMENT ON TABLE domrf_kn_objects IS 'Per-object (ЖК) catalogue from DOM.RF kn-API list endpoint';
|
||||||
|
|
||||||
|
-- Building specs (8)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS first_floor_type text,
|
||||||
|
ADD COLUMN IF NOT EXISTS section_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS elevators_passenger_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS elevators_cargo_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS parking_total_slots int,
|
||||||
|
ADD COLUMN IF NOT EXISTS guest_parking_inside_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS guest_parking_outside_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS ceiling_height_m numeric(3,2);
|
||||||
|
|
||||||
|
-- Apartment summary (3)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS finishing_variants_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS has_free_planning bool,
|
||||||
|
ADD COLUMN IF NOT EXISTS avg_flat_area_m2 numeric(5,1);
|
||||||
|
|
||||||
|
-- Yard infrastructure (4)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS playground_kids_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS playground_sport_count int,
|
||||||
|
ADD COLUMN IF NOT EXISTS has_bike_paths bool,
|
||||||
|
ADD COLUMN IF NOT EXISTS trash_areas_count int;
|
||||||
|
|
||||||
|
-- Accessibility / OVZ (3)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS has_ramp bool,
|
||||||
|
ADD COLUMN IF NOT EXISTS has_low_platforms bool,
|
||||||
|
ADD COLUMN IF NOT EXISTS has_wheelchair_lift bool;
|
||||||
|
|
||||||
|
-- Catalog / UI (9)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS flat_area_min numeric(8,2),
|
||||||
|
ADD COLUMN IF NOT EXISTS flat_area_max numeric(8,2),
|
||||||
|
ADD COLUMN IF NOT EXISTS price_min_rub bigint,
|
||||||
|
ADD COLUMN IF NOT EXISTS price_max_rub bigint,
|
||||||
|
ADD COLUMN IF NOT EXISTS price_per_m2_min numeric(12,2),
|
||||||
|
ADD COLUMN IF NOT EXISTS price_per_m2_max numeric(12,2),
|
||||||
|
ADD COLUMN IF NOT EXISTS parking_provision_pct numeric(5,1),
|
||||||
|
ADD COLUMN IF NOT EXISTS project_published_at date,
|
||||||
|
ADD COLUMN IF NOT EXISTS project_declaration_num text;
|
||||||
|
|
||||||
|
-- Metro & scores (5) — 22h merged into 22e
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS metro_nearest_name text,
|
||||||
|
ADD COLUMN IF NOT EXISTS metro_nearest_walk_minutes int,
|
||||||
|
ADD COLUMN IF NOT EXISTS metro_top3 jsonb,
|
||||||
|
ADD COLUMN IF NOT EXISTS domrf_score_location int,
|
||||||
|
ADD COLUMN IF NOT EXISTS domrf_score_transport int,
|
||||||
|
ADD COLUMN IF NOT EXISTS domrf_score_infrastructure int;
|
||||||
|
|
||||||
|
-- Developer split (1)
|
||||||
|
ALTER TABLE domrf_kn_objects
|
||||||
|
ADD COLUMN IF NOT EXISTS dev_group_name text;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.section_count
|
||||||
|
IS '22e: подъезды (residentialBuildings из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.project_published_at
|
||||||
|
IS '22e: дата публикации проекта (objPublDt из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.project_declaration_num
|
||||||
|
IS '22e: номер проектной декларации (rpdNum из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.price_min_rub
|
||||||
|
IS '22e: минимальная цена (freeFlatsInfo.priceMin из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.metro_nearest_name
|
||||||
|
IS '22h: ближайшая станция метро (metro.name из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.metro_nearest_walk_minutes
|
||||||
|
IS '22h: время пешком до метро, мин (metro.time из kn-API, rounded)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.metro_top3
|
||||||
|
IS '22h: топ-3 ближайших станций [{name,time,line,color,isWalk}], сейчас 1 элемент';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.dev_group_name
|
||||||
|
IS '22e: группа компаний (developer.groupName из kn-API)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.domrf_score_location
|
||||||
|
IS '22e: оценка расположения от DOM.RF (catalog 22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.domrf_score_transport
|
||||||
|
IS '22e: оценка транспорта от DOM.RF (catalog 22d)';
|
||||||
|
COMMENT ON COLUMN domrf_kn_objects.domrf_score_infrastructure
|
||||||
|
IS '22e: оценка инфраструктуры от DOM.RF (catalog 22d)';
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
-- 22g — domrf_developers +4 metrics
|
||||||
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
COMMENT ON TABLE domrf_developers IS 'Застройщики DOM.RF (companyGroup-level)';
|
||||||
|
|
||||||
|
ALTER TABLE domrf_developers
|
||||||
|
ADD COLUMN IF NOT EXISTS houses_building int,
|
||||||
|
ADD COLUMN IF NOT EXISTS houses_building_delayed int,
|
||||||
|
ADD COLUMN IF NOT EXISTS houses_delivered int,
|
||||||
|
ADD COLUMN IF NOT EXISTS houses_delivered_delayed int;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN domrf_developers.houses_building
|
||||||
|
IS '22g: домов строится (из карточки застройщика, catalog 22d)';
|
||||||
|
COMMENT ON COLUMN domrf_developers.houses_building_delayed
|
||||||
|
IS '22g: строится с задержкой';
|
||||||
|
COMMENT ON COLUMN domrf_developers.houses_delivered
|
||||||
|
IS '22g: сдано домов';
|
||||||
|
COMMENT ON COLUMN domrf_developers.houses_delivered_delayed
|
||||||
|
IS '22g: сдано с задержкой';
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
Loading…
Add table
Reference in a new issue