Commit graph

475 commits

Author SHA1 Message Date
Viktor Barzin
15dbcfa332
Parallelize frontend tests in Drone pipeline with 4 shards
Split the monolithic "build and test" kaniko step into a DAG:
tests run in 4 parallel shards (vitest --shard) alongside the
Docker image build, gated by a shared npm ci step. The kaniko
build now targets the named 'production' stage to skip the
in-Dockerfile test stage.
2026-02-21 21:02:22 +00:00
Viktor Barzin
a36c0dfdf7
Enable BuildKit in Drone API pipeline 2026-02-21 20:51:30 +00:00
Viktor Barzin
aea4b3c008
Expand frontend .dockerignore to exclude build artifacts 2026-02-21 19:51:04 +00:00
Viktor Barzin
231552c5e0
Remove unused frontend deps, move @types to devDependencies 2026-02-21 19:50:54 +00:00
Viktor Barzin
a122a7983a
Optimize Dockerfile: uv, BuildKit cache mounts, non-root user, healthcheck
- Replace pip with uv for 10-25x faster dependency installation
- Add BuildKit cache mounts for apt and uv caches across builds
- Add non-root appuser for improved container security
- Add HEALTHCHECK directive for container orchestration
- Add curl to runtime for healthcheck support
- Remove libgl1 (unused), add syntax directive for BuildKit
2026-02-21 19:49:11 +00:00
Viktor Barzin
c762d5a0a6
Optimize frontend Dockerfile: npm cache mount, non-root nginx, healthcheck 2026-02-21 19:49:06 +00:00
Viktor Barzin
795e1f7b57
Regenerate requirements.txt after dependency cleanup
- Remove pandas, apprise and their transitive dependencies
- Add opencv-python-headless, httpx to prod requirements
2026-02-21 19:47:15 +00:00
Viktor Barzin
3d9550c7f1
Replace pandas with stdlib csv, apprise with direct Slack webhook, switch to opencv-headless
- Rewrite csv_exporter.py to use stdlib csv.DictWriter instead of pandas DataFrame
- Rewrite notifications.py to use aiohttp direct Slack webhook instead of apprise
- Switch opencv-python to opencv-python-headless in pyproject.toml
- Move httpx from dev to prod dependencies
- Remove pandas and apprise from mypy ignore_missing_imports
2026-02-21 19:47:10 +00:00
Viktor Barzin
cde3540a1e
Remove watchdog and tqdm dependencies, replace with logging
- Remove watchdog (unused) and tqdm from pyproject.toml dependencies
- Replace tqdm.gather() with asyncio.gather() + logger.info() in
  image_fetcher, floorplan_detector, and route_calculator services
- Replace tqdm progress bar with logger.info() in listing_repository
- Remove tqdm from mypy ignore_missing_imports overrides
2026-02-21 19:39:49 +00:00
Viktor Barzin
d488208a26
Add build optimization implementation plan 2026-02-21 19:36:33 +00:00
Viktor Barzin
54c0999b94
Add build optimization design doc 2026-02-21 19:33:26 +00:00
Viktor Barzin
4deed9911c
Add photo carousel to listing cards and fix tap-to-detail
Backend: include first 5 photo URLs from additional_info in GeoJSON
streaming response, with fallback to photo_thumbnail.

Frontend: replace single thumbnail with swipeable embla-carousel on
compact cards. Remove window.open on card tap so clicking opens the
detail bottom sheet instead of navigating to Rightmove.
2026-02-21 19:19:32 +00:00
Viktor Barzin
f2e8d7d9f9
docs: add card carousel and tap-to-detail implementation plan [ci skip] 2026-02-21 19:10:06 +00:00
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