feat(analytics): add Python NeighborhoodScore service + NestJS HTTP proxy (TEC-2756)

- libs/ai-services: new POST /neighborhood/score router computing weighted
  6-axis livability score from per-category POI counts; algorithm versioned
  for future iteration (sigmoid curves, percentile thresholds).
- apps/api: HttpNeighborhoodScoreService proxies to Python first, falls back
  to PrismaNeighborhoodScoreService when AI service unavailable. Mirrors the
  HttpAVMService pattern. Existing GET /analytics/neighborhoods/:district/score
  endpoint and CQRS handler now flow through the proxy.
- AnalyticsModule binds Http variant by default, retains Prisma variant as
  injectable fallback.
- Tests: 5 pytest cases for Python heuristic, 4 vitest cases for HTTP proxy
  fallback behaviour.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-18 15:07:02 +07:00
parent 329a821b4a
commit 2c1e3771e9
9 changed files with 551 additions and 93 deletions

View File

@@ -23,7 +23,10 @@ import { PrismaValuationRepository } from './infrastructure/repositories/prisma-
import { AI_SERVICE_CLIENT, AiServiceClient } from './infrastructure/services/ai-service.client';
import { HttpAVMService } from './infrastructure/services/http-avm.service';
import { MarketIndexCronService } from './infrastructure/services/market-index-cron.service';
import { NeighborhoodScoreServiceImpl } from './infrastructure/services/neighborhood-score.service';
import {
HttpNeighborhoodScoreService,
PrismaNeighborhoodScoreService,
} from './infrastructure/services/neighborhood-score.service';
import { PrismaAVMService } from './infrastructure/services/prisma-avm.service';
import { AnalyticsController } from './presentation/controllers/analytics.controller';
import { AvmController } from './presentation/controllers/avm.controller';
@@ -66,8 +69,9 @@ const EventHandlers = [
PrismaAVMService,
{ provide: AVM_SERVICE, useClass: HttpAVMService },
// Neighborhood scoring
{ provide: NEIGHBORHOOD_SCORE_SERVICE, useClass: NeighborhoodScoreServiceImpl },
// Neighborhood scoring: HTTP proxy → Python AI service, falls back to Prisma scoring
PrismaNeighborhoodScoreService,
{ provide: NEIGHBORHOOD_SCORE_SERVICE, useClass: HttpNeighborhoodScoreService },
// Cron
MarketIndexCronService,