feat(22begh): schema extension — flats+6 / objects+30 / developers+4 / metro_top3 #306

Merged
lekss361 merged 2 commits from fix/22b-e-g-h-schema-extension-worktree into main 2026-05-17 15:27:00 +00:00
Owner

Summary

Issue #297 sub-tasks 22b + 22e + 22g + 22h combined (5d). Все touch domrf_kn.py + 1 SQL migration. Combined чтобы избежать конфликтов в scraper file.

Changes

  • data/sql/113_22begh_kn_schema_extension.sql (NN renamed 112→113, collision с PR #305 22i):
    • 22b domrf_kn_flats +6 cols: section_no, finishing_type, ceiling_height_m, key_handover_dt, catalog_updated_at, catalog_url_hash
    • 22e domrf_kn_objects +30 cols: building specs (8) + apartment summary (3) + yard infra (4) + ОВЗ (3) + catalog/UI (9) + metro & scores (5) + developer split (1)
    • 22h metro_top3 jsonb, metro_nearest_name, metro_nearest_walk_minutes — склеено с 22e (1 ALTER)
    • 22g domrf_developers +4 metrics: houses_building / houses_building_delayed / houses_delivered / houses_delivered_delayed
    • All ADD COLUMN IF NOT EXISTS (idempotent)
    • BEGIN…COMMIT transaction
  • backend/app/services/scrapers/domrf_kn.py:
    • _norm_flat() — добавлен mapping для 6 новых flat fields (NULL для catalog-only fields до 22d catalog scraper)
    • _norm_object() — mapping для 30 новых object fields (NULL для fields где payload пусто)
    • UPSERT_FLAT_SQL + UPSERT_OBJECT_SQL — ON CONFLICT clause с COALESCE для новых полей (preserves existing при NULL incoming)

Notes

  • Поля catalog-уровня (finishing_type, elevators_*, parking_total_slots) останутся NULL до 22d catalog scraper merge. Mapping ready.
  • metro_top3 jsonb будет заполняться когда payload audit показывает metro key (если нет в kn-API — нужен запрос на /metro endpoint, future task).

Test plan

  • After migration: \d domrf_kn_objects показывает все 30 новых колонок
  • After scrape run для obj_id=65136: проверить какие новые поля заполнились (audit numbers)
  • Existing scrape не сломан (COALESCE preserves old values)

Related: issue #297 sub-tasks 22b/22e/22g/22h · Phase 2-3 schema · ~5 days effort

## Summary Issue #297 sub-tasks **22b + 22e + 22g + 22h** combined (5d). Все touch `domrf_kn.py` + 1 SQL migration. Combined чтобы избежать конфликтов в scraper file. ## Changes - `data/sql/113_22begh_kn_schema_extension.sql` (NN renamed 112→113, collision с PR #305 22i): - **22b** `domrf_kn_flats` +6 cols: section_no, finishing_type, ceiling_height_m, key_handover_dt, catalog_updated_at, catalog_url_hash - **22e** `domrf_kn_objects` +30 cols: building specs (8) + apartment summary (3) + yard infra (4) + ОВЗ (3) + catalog/UI (9) + metro & scores (5) + developer split (1) - **22h** `metro_top3 jsonb`, `metro_nearest_name`, `metro_nearest_walk_minutes` — склеено с 22e (1 ALTER) - **22g** `domrf_developers` +4 metrics: houses_building / houses_building_delayed / houses_delivered / houses_delivered_delayed - All `ADD COLUMN IF NOT EXISTS` (idempotent) - BEGIN…COMMIT transaction - `backend/app/services/scrapers/domrf_kn.py`: - `_norm_flat()` — добавлен mapping для 6 новых flat fields (NULL для catalog-only fields до 22d catalog scraper) - `_norm_object()` — mapping для 30 новых object fields (NULL для fields где payload пусто) - `UPSERT_FLAT_SQL` + `UPSERT_OBJECT_SQL` — ON CONFLICT clause с COALESCE для новых полей (preserves existing при NULL incoming) ## Notes - Поля catalog-уровня (finishing_type, elevators_*, parking_total_slots) останутся NULL до 22d catalog scraper merge. Mapping ready. - `metro_top3 jsonb` будет заполняться когда payload audit показывает metro key (если нет в kn-API — нужен запрос на /metro endpoint, future task). ## Test plan - [ ] After migration: `\d domrf_kn_objects` показывает все 30 новых колонок - [ ] After scrape run для obj_id=65136: проверить какие новые поля заполнились (audit numbers) - [ ] Existing scrape не сломан (COALESCE preserves old values) Related: issue #297 sub-tasks 22b/22e/22g/22h · Phase 2-3 schema · ~5 days effort
lekss361 added 2 commits 2026-05-17 15:21:32 +00:00
SQL migration 112: ADD COLUMN IF NOT EXISTS для trёх таблиц (idempotent).
Scraper: _norm_flat section_no, _norm_object metro/freeFlatsInfo/dev_group_name,
UPSERT SQL расширены с COALESCE ON CONFLICT для всех новых полей.
CAST(:metro_top3 AS jsonb) — canonical psycopg3 bind для jsonb.
Поля catalog scraper 22d проставлены NULL-заглушками для будущего COALESCE.

Closes #297
lekss361 merged commit 6612c1cd2b into main 2026-05-17 15:27:00 +00:00
Author
Owner

Merged via deep-code-reviewer — verdict APPROVE.

Что проверено:

  • Migration 113_ sequential (после 112_22i), BEGIN/COMMIT, все ADD COLUMN IF NOT EXISTS — идемпотентно.
  • ALTER TABLE ... ADD COLUMN для nullable без default — constant-time, таблицу не rewrite'ит.
  • metro_top3 jsonb parsing safe: guard на isinstance(dict) + metro.name, float(raw_time) в try/except, json.dumps(..., ensure_ascii=False) + CAST(:metro_top3 AS jsonb) (no :: trap).
  • _g() / _to_date() / _to_bool() defensive — корректно вернут None на пустом payload.
  • UPSERT pattern с COALESCE(EXCLUDED.X, table.X) — preserves existing values при NULL incoming (тот же pattern что в PR #302).
  • v_domrf_objects_with_sales использует SELECT o.* — PostgreSQL re-resolves * at query time, новые колонки visible после ALTER без rebuild VIEW.
  • COMMENT ON COLUMN — основные поля документированы.
  • No CAST :x::type trap.
  • No SQL injection vectors, no hardcoded secrets.

Cross-file impact: PR #292 VIEW v_domrf_objects_with_sales — OK, PR #302 fields (flat_number/living_area/wall_type/energy_eff) — no collision (отдельные колонки), PR #305 (22i) — naming 113_ корректный.

Smoke после deploy:

  • \d domrf_kn_flats — все 6 новых колонок (section_no, finishing_type, ceiling_height_m, key_handover_dt, catalog_updated_at, catalog_url_hash).
  • \d domrf_kn_objects — все 30 новых колонок.
  • \d domrf_developers — все 4 metrics.
  • После scrape obj_id=65136: какие из 22e fields заполнились (audit на coverage). Ожидаемо заполнятся: section_count, project_declaration_num, project_published_at, price_min_rub, dev_group_name, metro_nearest_name, metro_nearest_walk_minutes, metro_top3.
  • Catalog-only поля (finishing_type, elevators_*, parking_total_slots, domrf_score_*, houses_*) останутся NULL — будут заполнены в 22d catalog scraper.
Merged via deep-code-reviewer — verdict APPROVE. **Что проверено:** - Migration `113_` sequential (после `112_22i`), BEGIN/COMMIT, все `ADD COLUMN IF NOT EXISTS` — идемпотентно. - `ALTER TABLE ... ADD COLUMN` для nullable без default — constant-time, таблицу не rewrite'ит. - `metro_top3 jsonb` parsing safe: guard на `isinstance(dict)` + `metro.name`, `float(raw_time)` в try/except, `json.dumps(..., ensure_ascii=False)` + `CAST(:metro_top3 AS jsonb)` (no `::` trap). - `_g()` / `_to_date()` / `_to_bool()` defensive — корректно вернут None на пустом payload. - UPSERT pattern с `COALESCE(EXCLUDED.X, table.X)` — preserves existing values при NULL incoming (тот же pattern что в PR #302). - `v_domrf_objects_with_sales` использует `SELECT o.*` — PostgreSQL re-resolves `*` at query time, новые колонки visible после ALTER без rebuild VIEW. - COMMENT ON COLUMN — основные поля документированы. - No CAST `:x::type` trap. - No SQL injection vectors, no hardcoded secrets. **Cross-file impact:** PR #292 VIEW `v_domrf_objects_with_sales` — OK, PR #302 fields (flat_number/living_area/wall_type/energy_eff) — no collision (отдельные колонки), PR #305 (22i) — naming `113_` корректный. **Smoke после deploy:** - `\d domrf_kn_flats` — все 6 новых колонок (section_no, finishing_type, ceiling_height_m, key_handover_dt, catalog_updated_at, catalog_url_hash). - `\d domrf_kn_objects` — все 30 новых колонок. - `\d domrf_developers` — все 4 metrics. - После scrape `obj_id=65136`: какие из 22e fields заполнились (audit на coverage). Ожидаемо заполнятся: `section_count`, `project_declaration_num`, `project_published_at`, `price_min_rub`, `dev_group_name`, `metro_nearest_name`, `metro_nearest_walk_minutes`, `metro_top3`. - Catalog-only поля (`finishing_type`, `elevators_*`, `parking_total_slots`, `domrf_score_*`, `houses_*`) останутся NULL — будут заполнены в 22d catalog scraper.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#306
No description provided.