feat(velocity): migrate D2 sales source → objective_corpus_room_month #158

Merged
lekss361 merged 3 commits from feat/migrate-velocity-objective-v2 into main 2026-05-15 06:34:00 +00:00
lekss361 commented 2026-05-15 06:25:01 +00:00 (Migrated from github.com)

Replaces closed PR #157 (had history conflict with squash-merged #155).

Why

D2 velocity returns null для prod cad — `domrf_kn_sale_graph` не обновлялся с 2026-01 (4+ мес stale).

`objective_corpus_room_month` weekly updated (newest 2026-05). Antonov's Objective integration (#94 / event Objective_Integration_May07).

Source Newest Rows w/ deals 6мо Deals
objective_corpus_room_month 2026-05 3486 12742
domrf_kn_sale_graph 2026-01 849 11319

Changes

velocity.py — 2 queries migrated

  • sales_rows: JOIN `objective_complex_mapping` (domrf_obj_id → project_name) → `objective_corpus_room_month`
  • _get_ekb_median: same; percentile from per_project velocity
  • Both wrapped в `with db.begin_nested()` (SAVEPOINT)
  • COALESCE(deals_total_vol_m2, deals_total_count * 45.0) — NULL safety for DKP-only rows

analytics_queries.py — 4 functions migrated

  • `_velocity_baseline`, `_district_velocity_trend`, `_elasticity_coef`, `_elasticity_per_bucket_coef`
  • KEPT on sale_graph (intentionally): `object_sale_graph` (admin history), `prinzip_objects_with_velocity` (separate task)
  • `velocity_source` label: → `"objective"`

Frontend — TS union + UI

  • `types/analytics.ts`: union → `"objective" | "sale_graph" | "rosreestr_fallback"`
  • `RecommendVelocityPanel.tsx`: handle both objective и sale_graph
  • `recommend/page.tsx`: KpiCard "Покрытие velocity" (general label)

Pre-push code-reviewer applied

3 findings caught locally до bot review:

  • 🔴 Frontend TS union missing "objective" → fixed
  • 🟠 NULL vol_m2 silent SUM → COALESCE fallback
  • 🟢 room_bucket parking filter → verified FALSE positive (all 5 buckets apartments)

Coverage caveat

`objective_complex_mapping` имеет 114 mappings (2.5% от 4548 ЖК ДомРФ). Для test cad — 3/32 конкурентов mapped → velocity вернёт result (confidence=low). Расширение coverage — future task.

E2E test after merge

```bash
curl -X POST "https://gendsgn.ru/api/v1/parcels/66:41:0204016:10/analyze?profile_user_id=admin"

expect velocity != null, competitors_count >= 1

```

Closes #156

**Replaces closed PR #157** (had history conflict with squash-merged #155). ## Why D2 velocity returns null для prod cad — \`domrf_kn_sale_graph\` не обновлялся с 2026-01 (4+ мес stale). \`objective_corpus_room_month\` weekly updated (newest 2026-05). Antonov's Objective integration (#94 / event Objective_Integration_May07). | Source | Newest | Rows w/ deals 6мо | Deals | |---|---|---|---| | objective_corpus_room_month | **2026-05** | 3486 | 12742 | | domrf_kn_sale_graph | 2026-01 | 849 | 11319 | ## Changes ### velocity.py — 2 queries migrated - **sales_rows**: JOIN \`objective_complex_mapping\` (domrf_obj_id → project_name) → \`objective_corpus_room_month\` - **_get_ekb_median**: same; percentile from per_project velocity - Both wrapped в \`with db.begin_nested()\` (SAVEPOINT) - **COALESCE(deals_total_vol_m2, deals_total_count * 45.0)** — NULL safety for DKP-only rows ### analytics_queries.py — 4 functions migrated - \`_velocity_baseline\`, \`_district_velocity_trend\`, \`_elasticity_coef\`, \`_elasticity_per_bucket_coef\` - KEPT on sale_graph (intentionally): \`object_sale_graph\` (admin history), \`prinzip_objects_with_velocity\` (separate task) - \`velocity_source\` label: → \`\"objective\"\` ### Frontend — TS union + UI - \`types/analytics.ts\`: union → \`\"objective\" | \"sale_graph\" | \"rosreestr_fallback\"\` - \`RecommendVelocityPanel.tsx\`: handle both objective и sale_graph - \`recommend/page.tsx\`: KpiCard \"Покрытие velocity\" (general label) ## Pre-push code-reviewer applied 3 findings caught locally до bot review: - 🔴 Frontend TS union missing \"objective\" → fixed - 🟠 NULL vol_m2 silent SUM → COALESCE fallback - 🟢 room_bucket parking filter → verified FALSE positive (all 5 buckets apartments) ## Coverage caveat \`objective_complex_mapping\` имеет 114 mappings (2.5% от 4548 ЖК ДомРФ). Для test cad — 3/32 конкурентов mapped → velocity вернёт result (confidence=low). Расширение coverage — future task. ## E2E test after merge \`\`\`bash curl -X POST \"https://gendsgn.ru/api/v1/parcels/66:41:0204016:10/analyze?profile_user_id=admin\" # expect velocity != null, competitors_count >= 1 \`\`\` Closes #156
lekss361 commented 2026-05-15 06:33:55 +00:00 (Migrated from github.com)

Final review (SHA 5c92647, CI )

Cross-checked через general-purpose agent (полный verification 7 пунктов). Verdict: approve + merge.

Verified by agent

JOIN semantics: domrf_kn_objects.obj_idobjective_complex_mapping.domrf_obj_idobjective_complex_name = crm.project_name — корректно per data/sql/68_schema_objective.sql lines 311-321
Per-corpus → per-ЖК collapse: GROUP BY m.obj_id корректно агрегирует corpora обратно в signal который callers ждут
ЕКБ filter implicit через Objective coverage — documented в docstring
Frontend contract preserved: obj_id, name, obj_class, distance_m, total_sqm_period всё spread через competitor_meta (velocity.py:248-263). Никакой регрессии vs PR #147 area
TS union extended корректно: "objective" | "sale_graph" | "rosreestr_fallback" + UI handles обе labels
SAVEPOINT integrity: все 3 db.execute блока в velocity.py обёрнуты with db.begin_nested():. NO db.rollback() calls (PR #155 fix сохранён). Inline comment at lines 87-88 документирует.
analytics_queries.py 4 функции: plain reads, не savepoint-critical path — отсутствие begin_nested правильно (matches pre-PR pattern)
Schema deps: objective_corpus_room_month + objective_complex_mapping уже в prod через migration 68. NO new SQL в этом PR ✓
Zero-mapped fallback: empty sales_rowsmonths_observed=0 → clean return None, matches existing test
NULL safety: COALESCE(deals_total_vol_m2, deals_total_count * 45.0) для DKP-only rows

Minor (не блокеры)

  • region_code retained в signatures как _ = region_code — slightly ugly но pragmatic backward-compat
  • Coverage 2.5% (114/4548) ЖК mapped — большинство parcels confidence=low. Future task для expansion documented.

Author quality

Автор сам прогнал code-reviewer pre-push и поймал 3 issue до open PR — высокий стандарт.

approve merge

## Final review (SHA `5c92647`, CI ✅) Cross-checked через `general-purpose` agent (полный verification 7 пунктов). Verdict: **approve + merge**. ### Verified by agent ✅ **JOIN semantics**: `domrf_kn_objects.obj_id` → `objective_complex_mapping.domrf_obj_id` → `objective_complex_name = crm.project_name` — корректно per `data/sql/68_schema_objective.sql` lines 311-321 ✅ **Per-corpus → per-ЖК collapse**: `GROUP BY m.obj_id` корректно агрегирует corpora обратно в signal который callers ждут ✅ **ЕКБ filter implicit** через Objective coverage — documented в docstring ✅ **Frontend contract preserved**: `obj_id`, `name`, `obj_class`, `distance_m`, `total_sqm_period` всё spread через `competitor_meta` (velocity.py:248-263). Никакой регрессии vs PR #147 area ✅ **TS union extended корректно**: `"objective" | "sale_graph" | "rosreestr_fallback"` + UI handles обе labels ✅ **SAVEPOINT integrity**: все 3 `db.execute` блока в velocity.py обёрнуты `with db.begin_nested():`. NO `db.rollback()` calls (PR #155 fix сохранён). Inline comment at lines 87-88 документирует. ✅ **analytics_queries.py 4 функции**: plain reads, не savepoint-critical path — отсутствие begin_nested правильно (matches pre-PR pattern) ✅ **Schema deps**: `objective_corpus_room_month` + `objective_complex_mapping` уже в prod через migration 68. **NO new SQL** в этом PR ✓ ✅ **Zero-mapped fallback**: empty `sales_rows` → `months_observed=0` → clean `return None`, matches existing test ✅ **NULL safety**: `COALESCE(deals_total_vol_m2, deals_total_count * 45.0)` для DKP-only rows ### Minor (не блокеры) - `region_code` retained в signatures как `_ = region_code` — slightly ugly но pragmatic backward-compat - Coverage 2.5% (114/4548) ЖК mapped — большинство parcels confidence=low. Future task для expansion documented. ### Author quality Автор сам прогнал code-reviewer pre-push и поймал 3 issue до open PR — высокий стандарт. approve merge
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#158
No description provided.