Commit graph

412 commits

Author SHA1 Message Date
Viktor Barzin
089ee88728
docs: add card carousel and tap-to-detail design [ci skip] 2026-02-21 19:08:12 +00:00
Viktor Barzin
a153f64af4
Format available_from dates as human-readable on listing cards
Display dates like "22 Jun 2025" instead of raw ISO timestamps.
2026-02-21 18:02:14 +00:00
Viktor Barzin
578b97b0c5
Add configurable request timeout and retry on TimeoutError
Requests to Rightmove API previously had no explicit timeout, causing
hung connections to block workers indefinitely. Add a configurable
request_timeout (default 30s) to ScraperConfig and apply it to all
aiohttp sessions. Also retry on TimeoutError in addition to
ThrottlingError for all API query functions.
2026-02-21 17:50:36 +00:00
Viktor Barzin
7a1042741e
fix: remove duplicate alembic migration causing multiple heads on startup 2026-02-21 16:08:02 +00:00
Viktor Barzin
1536e98c8b
fix: skip frontend config tests when frontend/ dir absent (Docker build) 2026-02-21 15:52:43 +00:00
Viktor Barzin
43f9d210fb
Fix tests to match decision service API and add filtering to non-streaming endpoint
- Update test mocks from _get_disliked_ids to _get_user_id_safe
- Fix decision service test method names (clear_decision -> remove_decision, etc.)
- Fix positional vs keyword arg assertion in set_decision test
- Add decision_filter param to non-streaming listing_geojson endpoint
2026-02-21 15:52:31 +00:00
Viktor Barzin
a2745c1478
Add tappable cards, detail bottom sheet, swipe gestures, and favorites view
- Decision types, services (decisionService, listingDetailService), and index exports
- useDecisions hook with optimistic updates and Map-based state
- useListingDetail hook with session-level caching
- PhotoCarousel component using embla-carousel-react
- ListingDetail component with full property info, like/dislike buttons
- ListingDetailSheet using vaul Drawer (slide-up bottom sheet)
- SwipeablePropertyCard with @use-gesture/react and @react-spring/web
- SwipeReviewMode for mobile full-screen swipe review
- FavoritesView with virtualized liked listings and remove button
- App.tsx integration: decision state, client-side disliked filtering, detail sheet, swipe handlers
- ListView conditionally renders SwipeablePropertyCard when handlers provided
- StatsBar adds 'saved' view mode with heart icon
- Header adds liked count indicator
- New deps: vaul, embla-carousel-react, @use-gesture/react, @react-spring/web
2026-02-21 15:49:15 +00:00
Viktor Barzin
9e1beb7495
Add listing decisions (like/dislike) backend with detail endpoint
- ListingDecision model with unique constraint on (user_id, listing_id, listing_type)
- Alembic migration for listingdecision table
- DecisionRepository with dialect-aware upsert (MySQL/SQLite)
- DecisionService with input validation
- Decision API routes: PUT/GET/DELETE on /api/decisions
- GET /api/listing/{id}/detail endpoint extracting full property info from additional_info
- Add listing ID to GeoJSON feature properties
- Decision filtering on GeoJSON stream endpoint (decision_filter param)
2026-02-21 15:49:10 +00:00
Viktor Barzin
a2e7d59af2
Add .worktrees/ to .gitignore for git worktree isolation 2026-02-21 15:48:34 +00:00
Viktor Barzin
318385035e
fix: revert CI to use Docker Hub for caching — local registry is read-only pull-through cache 2026-02-21 15:25:36 +00:00
Viktor Barzin
b1be4d4170
perf: optimize CI pipeline — eliminate double dependency installs, use local registry cache
- Frontend Dockerfile: split into deps/test/builder/nginx stages so npm ci
  runs once (cached when package-lock.json unchanged), tests run in build
- Backend Dockerfile: add test stage that runs pytest inside the build,
  eliminating separate test image build
- .drone.yml: remove separate test steps (now inside Dockerfile builds),
  point cache_from/cache_repo at local registry (10.0.20.10:5000) instead
  of Docker Hub for faster layer cache pulls
2026-02-21 15:10:55 +00:00
Viktor Barzin
68859ae577
docs: add CI pipeline optimization design document
Merge tests into Dockerfiles to eliminate double dependency installs,
switch Docker layer caching to local registry at 10.0.20.10:5000.
2026-02-21 15:07:50 +00:00
Viktor Barzin
9c7368a969
fix: mock _get_disliked_ids and bypass rate limiter in existing tests
Tests were failing because the new decision filtering code in api/app.py
tries to query the database for disliked IDs, but test fixtures that mock
the ListingRepository didn't also mock _get_disliked_ids. Additionally,
rate limiter was not bypassed in TestListingGeoJsonEndpoint client fixture,
causing 429s when tests run in sequence.
2026-02-21 14:22:46 +00:00
Viktor Barzin
49280d9679
fix: QA fixes for property decisions feature
- Replace deprecated datetime.utcnow() with datetime.now(UTC) in model
  and repository
- Add listing_type validation to decision_service (RENT/BUY only)
- Fix decision filtering tests failing due to rate limiting by patching
  _match_endpoint
- Add SwipeCard component test suite (11 tests covering rendering,
  interactions, and POI distances)
- Add test for invalid listing_type validation
2026-02-21 14:04:34 +00:00
Viktor Barzin
8452f65d25
feat: filter disliked listings from GeoJSON endpoints
Both /api/listing_geojson and /api/listing_geojson/stream now exclude
disliked listings by default. A decision_filter='everything' param
bypasses filtering. 2 integration tests verify the behavior.
2026-02-21 13:57:43 +00:00
Viktor Barzin
43084ef19a
feat: add SavedView, integrate decisions into App with review mode and client-side filtering 2026-02-21 13:55:05 +00:00
Viktor Barzin
341de89004
feat: add decision API routes with tests
PUT /api/decisions/{listing_id} to set decision,
GET /api/decisions to list all user decisions,
DELETE /api/decisions/{listing_id} to remove a decision.
All 6 API route tests pass.
2026-02-21 13:54:38 +00:00
Viktor Barzin
d350b806ba
feat: add SwipeCard and SwipeReviewMode components with gesture support 2026-02-21 13:53:06 +00:00
Viktor Barzin
4877a5fc9f
feat: add decision_service with unit tests
Service layer provides validation, delegation to repository, and
get_disliked_listing_ids for filtering. All 7 unit tests pass.
2026-02-21 13:52:54 +00:00
Viktor Barzin
91c8c884d2
feat: add DecisionRepository with unit tests
Repository provides upsert, get, delete, and disliked-ID-set queries
for listing decisions. All 6 unit tests pass.
2026-02-21 13:52:20 +00:00
Viktor Barzin
2fdeebbae1
feat: add useDecisions hook with optimistic updates 2026-02-21 13:51:50 +00:00
Viktor Barzin
813f048e46
feat: add frontend decision service and types 2026-02-21 13:51:12 +00:00
Viktor Barzin
3d2a24e921
feat: add ListingDecision model and Alembic migration
Add ListingDecision SQLModel entity for tracking user like/dislike
decisions on properties, with unique constraint on (user_id, listing_id,
listing_type) and appropriate indexes.
2026-02-21 13:50:55 +00:00
Viktor Barzin
e7eb8523cb
feat: add react-spring and use-gesture dependencies 2026-02-21 13:50:38 +00:00
Viktor Barzin
3384798b34
docs: add property decisions Phase 1 implementation plan
16-task TDD plan covering backend (model, migration, repo, service,
API routes, decision filtering) and frontend (decision service, hook,
SwipeCard, SwipeReviewMode, SavedView, integration).
2026-02-21 13:46:19 +00:00
Viktor Barzin
941004cf1b
docs: add property decisions feature design document
Tinder-style swipe card UI for liking/disliking properties with
shared shortlists. Two-phase rollout: core decisions first, sharing second.
2026-02-21 13:41:30 +00:00
Viktor Barzin
13637ec881
fix: update tests for mobile responsive changes
- Add matchMedia polyfill to test setup (jsdom doesn't implement it)
- Remove Header tests for filter toggle (replaced by mobile FAB)
2026-02-21 11:45:50 +00:00
Viktor Barzin
a744b33578
feat: make frontend fully responsive with mobile-first layout
Add mobile-responsive design with full feature parity:
- Bottom sheet (vaul) with 3 snap points for map+list coexistence
- Swipeable property cards with horizontal scroll-snap
- Hamburger menu with health, tasks, user info
- Full-screen map with repositioned legend (top-left on mobile)
- Filter FAB opening Sheet drawer
- TaskProgressDrawer from bottom on mobile
- All changes gated behind useIsMobile() hook (768px breakpoint)
- Desktop layout completely untouched

New components: MobileBottomSheet, SwipeableCardRow,
PropertyCardCompact, MobileMenu

Also fixes: idempotent longitude migration, React hooks order
2026-02-21 11:34:53 +00:00
Viktor Barzin
8f068a581e
Fix frontend CI pipeline OOM kills and test timeouts
- Set memory limit to 2048MiB for the "Run frontend tests" step
  (node:24-alpine was OOM killed at the default 1Gi)
- Set memory limit to 2048MiB for the "Build frontend image" kaniko
  step (also OOM killed at 1Gi)
- Increase vitest testTimeout to 30s (CI runner is ~75x slower than
  local dev; tests were timing out at the default 5s)
2026-02-17 21:46:29 +00:00
Viktor Barzin
2d6726dcd7
Show last listing update time next to connection status in header
Add last_updated timestamp to /api/status endpoint by querying
MAX(last_seen) across both listing tables. Display it in the
HealthIndicator as relative time (e.g. "2h ago") with full
date/time in the tooltip on hover.
2026-02-17 19:54:15 +00:00
Viktor Barzin
7833bd3ecf
Add retry logic to Drone CI clone step for transient DNS failures
Disable the default clone and use a custom clone step with up to 5
retry attempts in both frontend and api pipelines. This prevents
builds from failing due to transient "Could not resolve host" errors.
2026-02-15 17:24:56 +00:00
Viktor Barzin
79586015f1
Remove pre-push hook that required Docker for tests 2026-02-15 15:48:01 +00:00
Viktor Barzin
6489012e8f
Auto-pan map to keep popup fully visible in viewport
When a property popup opens near the edge of the map, the map now
automatically pans so the entire popup is visible with 20px padding.
2026-02-15 15:27:51 +00:00
Viktor Barzin
f6f0aa8d14
Fix metrics not initialized in Celery prefork worker processes
worker_ready fires in the main process after pool workers are forked,
so init_metrics() never ran in the child processes. Add a
worker_process_init handler to initialize OTel metrics in each worker.
2026-02-15 12:58:59 +00:00
Viktor Barzin
51e7a7ccc5
Add setup-test-venv.sh for running tests without Docker [ci skip] 2026-02-14 11:44:49 +00:00
Viktor Barzin
556b5b0f42
Add local venv fallback for running tests in CLAUDE.md [ci skip] 2026-02-14 11:42:26 +00:00
Viktor Barzin
fb98e5c12e
Add pre-commit test requirement to CLAUDE.md [ci skip] 2026-02-14 11:38:06 +00:00
Viktor Barzin
dbabdf39d0
Add shared pre-push hook to run tests before pushing
Hook lives in .githooks/pre-push (tracked) and runs pytest inside the
Docker app container.  start.sh auto-configures core.hooksPath so new
clones pick it up on first run.
2026-02-14 11:36:26 +00:00
Viktor Barzin
25912eac0c
Fix metric imports: use module-level access instead of name imports
Type-annotated metric variables (e.g. `geojson_cache_operations: Counter`)
don't exist as importable names until init_metrics() runs.  Switch all
`from api.metrics import <metric>` to `import api.metrics as m` and
access instruments as attributes at runtime to avoid ImportError.
2026-02-14 11:21:49 +00:00
Viktor Barzin
d6edb747d2
Add structured JSON logging, OTel business metrics, and Grafana dashboard
Structured logging via JsonFormatter replaces uvicorn's default format so
Loki can parse timestamps and fields.  14 business metrics (scrape stats,
throttle events, circuit breaker state, cache hit rate, OCR success rate,
Celery task lifecycle) are defined in a shared metrics module and
instrumented across the scraper pipeline, API, and workers.  Celery
workers expose a Prometheus HTTP endpoint on configurable ports.
2026-02-14 10:59:12 +00:00
Viktor Barzin
a1829957c1
Auto-redirect to login on 401 API responses
When a session token expires, API calls return 401 but nothing caught
it — errors were shown as generic dialogs or swallowed. Now both
apiClient and streamingService detect 401 responses and clear auth
state, which causes App.tsx to render the login modal automatically.
2026-02-13 21:16:53 +00:00
Viktor Barzin
3acf8db7af
Switch frontend build to kaniko to avoid ephemeral-storage eviction
plugins/docker uses Docker-in-Docker which consumes significant
ephemeral storage for image layers. On nodes with disk pressure
(k8s-node1), the build pod gets evicted before completing.

Switch to plugins/kaniko which builds OCI images without a Docker
daemon, using significantly less ephemeral storage. Enable kaniko's
built-in layer caching via a dedicated cache repo.
2026-02-13 20:12:15 +00:00
Viktor Barzin
431e276d05
Remove cache builder stages to fix ephemeral-storage eviction
Both frontend and API pipelines had separate "Cache builder stage"
steps that built and pushed intermediate Docker targets purely for
layer caching. Running these alongside the actual build steps doubled
the Docker build work per pipeline, causing pods to be evicted when
the node hit its ephemeral-storage threshold (~20GB).

The "Build image" steps already use cache_from with the existing
:builder tags from previous builds, so the separate cache steps
are redundant for the common case (unchanged package files).
2026-02-13 19:52:17 +00:00
Viktor Barzin
41b7d221e4
Fix 7 bugs: security, memory leak, stale state, error handling
- WebSocket: verify task ownership before allowing subscribe (security)
- POI routes: replace assert with HTTPException for production safety
- cancel_task: return HTTP 404 instead of 200 for missing tasks
- routing_config: add descriptive ValueError for invalid env vars
- POIManager: show error feedback instead of silently swallowing failures
- VisualizationCard: reset POI/travel mode state on metric switch
- Map: clean up heatmap layers/sources on unmount to prevent memory leak
- Update test to expect 404 from cancel_task ownership check
2026-02-13 19:36:43 +00:00
Viktor Barzin
25c87da1cf
Move NODE_OPTIONS before npm ci and bump pod memory to 4Gi
npm ci can also OOM during dependency installation. Move the heap
limit before npm ci so it applies to all Node processes. Bump Drone
pod limits to 4Gi (requests 2Gi) to cover Docker-in-Docker overhead.
2026-02-11 23:11:55 +00:00
Viktor Barzin
fd4864fd03
Fix frontend build OOM: skip tsc, use vite only, bump memory limits
- Replace `npm run build` (tsc -b && vite build) with `npx vite build`
  in Dockerfile since Vite transpiles via SWC independently of tsc.
  Type-checking is already done in the test step.
- Set Node heap to 1024MB (was 384MB which OOMed even for Vite)
- Bump Drone pod memory: requests 1.5Gi, limits 3Gi to cover
  plugins/docker overhead
2026-02-11 22:59:12 +00:00
Viktor Barzin
f9e4960783
Fix Drone resource spec: use bytes instead of Gi suffix 2026-02-11 22:05:04 +00:00
Viktor Barzin
6492741757
Fix frontend Docker build OOM: limit Node heap + request more memory
- Set NODE_OPTIONS=--max-old-space-size=512 in Dockerfile to cap tsc
  heap usage within constrained CI pods
- Add resource requests (1Gi) and limits (2Gi) to frontend Docker
  build steps in Drone pipeline
2026-02-11 21:50:16 +00:00
Viktor Barzin
34f70b2ba4
Exclude test files from frontend production TypeScript build
Test files use Vitest globals (vi, describe, it, expect) which aren't
available to tsc during production builds. Exclude __tests__ dirs and
*.test.* / *.spec.* files from tsconfig.app.json so tsc -b succeeds.
2026-02-11 21:26:50 +00:00
Viktor Barzin
96bcebdb40
Cache test image to speed up CI backend test step
Add a 'test' stage to Dockerfile that extends runtime-base with the
venv and test dependencies (pytest, fakeredis, etc.) pre-installed.
Drone CI now builds and caches this image as :test, then uses it
directly for running tests — eliminating apt-get and pip install
on every build.
2026-02-10 22:57:42 +00:00