Commit Graph

207 Commits

Author SHA1 Message Date
wasrusgen
b1d8f3e38a gitignore: exclude .tmp_* files (accidentally committed proxy creds in previous commit)
User needs to rotate Proxys.io password after migration tests are done.
2026-05-11 16:06:05 +03:00
wasrusgen
e7f6e64e38 playwright_engine: route through proxy_pool — random residential IP per request
- New use_proxy param (default True)
- Per-request random proxy from pool
- _parse_proxy_url_for_playwright converts http://user:pass@host:port to playwright.proxy dict
2026-05-11 16:05:36 +03:00
wasrusgen
811bed31a4 backend: proxy_pool supports PROXY_LIST_FILE + format auto-conversion
- New env: PROXY_LIST_FILE — path to file with one proxy per line
- _normalize_proxy_entry accepts: http://user:pass@host:port, host:port:user:pass (Proxys.io format), host:port
- _load_from_file reads file, dedup with static list
- /api/proxy_status returns file_path, file_loaded count, sample (first 3 masked)
2026-05-11 15:52:02 +03:00
wasrusgen
d7be644aed miniapp: price comparison matrix as PRIMARY view per category
WHAT CHANGED:
- New _renderPriceMatrix(models) — table with rows=models, columns=stores
- Inserted as PRIMARY view above model cards (was secondary accordion)
- Columns dynamically include only stores that returned data
- Sticky model column (left) — scrolls horizontally on mobile
- Best price per row highlighted: green bg + ✓ badge + green text
- Empty cells: '—' if no URL, 'смотреть →' if URL but no price yet
- 'Мин' column on far right — explicit cheapest price summary

CSS:
- .report-matrix-wrap with rounded card
- Sticky col-model with box-shadow on right edge
- Cell-price.best with rgba green background
- .best-mark circle badge

PREVIEW:
- Updated mock with 3 fridges + 3 hobs across multiple stores (real pricing spread)
- Demonstrates min-price highlighting working

UX:
- User can now visually compare 'where is it cheapest' at a glance
- Tap any cell with price → opens store page
- Tap empty cell with URL → opens search in store

NEXT: same matrix can become PDF/Excel export for client briefcase
2026-05-11 14:56:41 +03:00
wasrusgen
ca342c0641 ai+report: deeper analysis — required pros/cons, category insights, source visibility
AI PROMPT (ai.py):
- Requires minimum 3 pros + 2 cons per model with NUMBERS (36 dB, 463 L, A++, не 'тихий/большой')
- New field 'reasoning' — 1-sentence why-this-model justification
- New per-category 'analysis' — 2-3 sentences about trade-offs
- Strict rules: no fake article numbers, account for parallel-import price markup
- Russian market 2026 awareness: Haier/Korting up, Bosch/Siemens ⚠

TELEGRAM FORMAT (main.py):
- Renders category analysis as italic prelude
- Lists pros/cons as bullet lists (up to 4 pros, 3 cons)
- Shows '🛒 Нашли в: OZON · Citilink · WB' line listing successful sources
- Rating + reviews + stores count line: '📊 ★ 4.7 · 1242 отзыв. · 12 магаз.'
- Direct link to best store: '🔗 Открыть в магазине'

WB PARSER:
- Generates 3 query variants per request: full → brand+model → model only
- Increases hit rate when AI search_query is too verbose
- First non-empty variant wins

MINIAPP REPORT (podbor.js + podbor.css):
- Category analysis block above models (italic, walnut left-border)
- Pros block: green tinted bg, bullet list, header 'Плюсы'
- Cons block: terracotta tinted bg, bullet list, header 'Минусы'
- Reasoning chip: 💡 italic in warm background
- Source badges with per-store price '<store> · 89 990 ₽'
- Color-coded source links: OZON blue, Citilink yellow, WB pink, Я.Маркет red, DNS orange
- 'X магазинов нашли товар' header + plural fix
- '— не найден' fallback if 0 sources

PREVIEW (preview-report.html):
- Mock updated with Haier as flagship (more relevant for 2026 RF)
- Shows analysis, reasoning, source spread (4 stores with different prices)
2026-05-11 14:34:08 +03:00
wasrusgen
4b04f2de54 miniapp: summary page hides Подключение/Вентиляция if hob/hood not picked 2026-05-11 14:26:12 +03:00
wasrusgen
80580db446 miniapp: 4 UX fixes from user feedback
1. PHONE NORMALIZATION
   - On blur (or before submit): '9001234567' -> '+7 900 123-45-67'
   - Handles 8XXX, 7XXX, +7XXX, 10-digit mobile prefixes
   - Leaves untouched if not Russian-looking number

2. BRAND LIST FOR RF 2026
   - PODBOR_SINGLE_BRAND_OPTIONS updated with realistic 2026 brands
   - Promoted: Haier, Korting, Midea, Hisense, Бирюса, Атлант, Pozis, DEXP
   - Bosch/Siemens marked with ⚠ (parallel-import)
   - Miele/Liebherr/Smeg also marked ⚠
   - PODBOR_BRANDS per-category fully refreshed

3. BUDGET ADAPTIVE HINTS
   - Hints now scale by selected categories share of full kitchen
   - Just fridge picked → 'Средний' shows ~88-175 тыс instead of 350-700к
   - Full 8 categories → original 350-700к
   - PODBOR_BUDGET_SHARES + PODBOR_BUDGET_RANGES constants

4. INFRA STEP CONDITIONAL
   - Stove power question only shown if hob category picked
   - Vent question only shown if hood category picked
   - If neither → step auto-skips to summary (with brief notice)
   - Summary 'Назад' button respects skip — goes to strategy if needed
2026-05-11 14:25:25 +03:00
wasrusgen
44281b1e07 citilink: dedup by product ID + filter Next.js placeholder images 2026-05-11 13:59:07 +03:00
wasrusgen
c5f662f53d citilink: rewrite parser to walk up from a[href*=/product/] (CSS-in-JS resistant) 2026-05-11 13:57:18 +03:00
wasrusgen
1a948ebf02 ozon: fix false-positive challenge detector (was catching 'challenge' in normal JS) 2026-05-11 13:54:13 +03:00
wasrusgen
e8b487891f backend: working parsers — OZON + Citilink (DOM via Playwright) + WB
DIAGNOSTIC RESULTS:
- OZON: 19 product links via Playwright on naked VPS-IP ✓
- Citilink: 112 data-meta-name Snippets ✓
- Wildberries: JSON API works with delays ✓
- Я.Маркет, DNS: blocked by ASN (need residential proxy)

OZON PARSER:
- Pure Playwright DOM (composer-api dropped — was blocked)
- Selects a[href*='/product/'], walks up to card div, extracts title/price/img
- Filters fake 'titles' like Распродажа, Скидка

CITILINK PARSER (new):
- Selects [data-meta-name*='Snippet'] or ProductCard markers
- Multiple title selectors fallback chain
- Filters out non-product hits

PARSERS/__init__.py:
- DEFAULT_SOURCES = (ozon, citilink, wb) — all work without proxy
- Я.Маркет, DNS kept but not default — usable when residential proxy added

NEW ENDPOINT:
- GET /api/parse_citilink?q=...&limit=N
2026-05-11 13:53:07 +03:00
wasrusgen
5fdae262ef backend: parse_* endpoints sync (FastAPI threadpool) — fix Playwright asyncio conflict 2026-05-11 13:30:51 +03:00
wasrusgen
d5f290bd0a backend: Playwright + Chromium for JS-rendered sites (Я.Маркет, OZON fallback)
DOCKERFILE:
- + Chromium system deps (libnss3, libxkbcommon0, libgbm1, libgtk-3-0, etc.)
- + RUN python -m playwright install chromium (~150MB)
- + ENV PLAYWRIGHT_BROWSERS_PATH

REQUIREMENTS:
- + playwright >= 1.45

PARSERS:
- new playwright_engine.py — singleton browser, isolated context per request,
  blocks images/fonts/CSS to save memory, waits for selector + JS hydration
- yamarket.py — rewritten to use Playwright (Я.Маркет is React SPA)
- ozon.py — Playwright fallback when composer-api returns challenge (403)
- wb.py — exponential backoff on 429, still uses direct HTTP (JSON API, no JS needed)

STRATEGY (Hybrid Path C):
- Я.Маркет: Playwright (rendering JS)
- OZON: composer-api first, Playwright fallback
- WB: direct HTTP with backoff (JSON API, fast)
- DNS: kept but lower priority (Qrator hard to crack)
- No more proxy needed for primary path

DEPLOY: removed PROXY_STATIC_LIST from .env, expect ~5min for first build (Chromium download)
2026-05-11 13:25:05 +03:00
wasrusgen
3ee5275ea0 backend: PROXY_STATIC_LIST support (manual proxies without API token)
- proxy_pool now loads from both PROXY_STATIC_LIST (env, comma-separated) and PROXY6_TOKEN (API)
- Static list has priority, merged with API list (dedup by URL)
- /api/proxy_status returns masked proxy URLs for diagnostic (passwords hidden)
- Supports formats: 'http://user:pass@host:port' or 'host:port' (assumed http://)
2026-05-11 13:03:29 +03:00
wasrusgen
c2be5e846f miniapp: inline report after submit + standalone preview-report.html
REPORT RENDERER (podbor.js):
- New renderReport(ai, leadId) function — beautiful inline report after submit success
- Shows by_category with up to 5 models per category
- Model card: photo (88x88), brand · name, price range, rating + reviews + stores
- Highlights (with tech translations), pros (green), cons (orange)
- External links to WB / Я.Маркет / OZON / DNS (when enriched data present)
- Comparison table per category (accordion details)
- Total price block (dark theme contrast)
- Warnings block (when AI returns concerns)

CSS (podbor.css):
- .report-* classes: head, summary, cat, model, links, compare, total, warnings
- Editorial Calm palette — walnut accents, paper bg, Newsreader for titles
- Responsive: model card grid 88px image + 1fr body
- Placeholder gradient when no image (camera emoji)

STANDALONE PREVIEW (preview-report.html):
- Mock AI response with 3 fridges + 2 hobs
- Same render logic, runs without backend
- Visit: https://wasrusgen.github.io/zov-tech/preview-report.html

NEXT: integrate proxy6 token → real photos/prices instead of placeholders
2026-05-11 12:26:58 +03:00
wasrusgen
82425dbd88 backend: Proxy6 pool + parsers WB / OZON / Я.Маркет / DNS
PROXY POOL (app/proxy_pool.py):
- Loads active proxies from Proxy6.net API every 10 min
- Random rotation per request via proxied_client(timeout, headers)
- Graceful fallback to direct HTTP if PROXY6_TOKEN not set
- Config: PROXY6_TOKEN env var

PARSERS (app/parsers/):
- dns.py — refactored to use proxy_pool with retry+rotation on Qrator block
- wb.py — Wildberries JSON API (search.wb.ru), retries on 429
- ozon.py — OZON composer-api JSON (widgetStates extraction)
- yamarket.py — Я.Маркет HTML + embedded JSON parser
- __init__.py — enrich_one() fans out to all sources, aggregates min/max prices, max rating, sum reviews
- enrich_models() — batch enrich for AI by_category output

NEW DIAGNOSTIC ENDPOINTS (main.py):
- GET /api/parse_wb?q=...&limit=N
- GET /api/parse_ozon?q=...&limit=N
- GET /api/parse_yamarket?q=...&limit=N
- GET /api/parse_all?q=... — fan-out + aggregate
- GET /api/proxy_status — pool diagnostics (count, token configured, age)

PODBOR (main.py):
- _enrich_ai_with_dns -> _enrich_ai_marketplaces (uses all sources)

DEPLOY: needs PROXY6_TOKEN in /opt/zov-tech/deploy/.env on VPS, then docker compose build + up -d backend
2026-05-11 12:18:04 +03:00
wasrusgen
64edb76035 backend: new state-shape AI prompt + DNS parser MVP
AI PROMPT (ai.py):
- Документирует новую форму checklist (per_cat.answers, brand_strategy, single_brand, brands, budget_preset, pick_strategies)
- Просит вернуть 3-5 моделей по КАЖДОЙ категории (не одну)
- Новый формат ответа: by_category[cat].models[] с brand/model/price_min/price_max/search_query/pros/cons/tier
- Подробные правила для бренд-стратегий (single → вся техника одной марки; different → preferred/acceptable/avoid)
- Бюджет-пресеты с авто-распределением по категориям (fridge ~25%, hob ~12% и т.д.)

DNS PARSER (parsers/dns.py):
- search_dns(query, limit) — HTTP + BeautifulSoup
- Реалистичный User-Agent, фолбэк на JSON-LD если HTML-селекторы не сработали
- enrich_models(models) — обогащает список моделей от AI, добавляя dns: {title, price, image, url, rating, reviews}
- Вежливая задержка 0.4с между запросами

MAIN.PY:
- /api/parse_dns?q=... — тестовый эндпоинт для проверки парсера
- _handle_podbor теперь после AI вызывает _enrich_ai_with_dns для каждой модели
- _format_podbor_for_telegram переписан под новый формат by_category — выводит 3-5 моделей в каждой категории с pros/cons
- Fallback на старый формат items[] для совместимости

REQUIREMENTS:
- + beautifulsoup4 >= 4.12
- + lxml >= 5.2

DEPLOY: после пуша на VPS нужно пересобрать backend контейнер (docker compose up --build -d backend)
2026-05-11 11:42:37 +03:00
wasrusgen
717c6ea138 miniapp: hierarchical wizard for all 8 categories + condition support
CATEGORIES MIGRATED to steps[] schema:
- hob: Источник нагрева → Подтип (multi, optionsBy) → Размер → Конфорки → Особенности
- oven: Установка → Функции (multi) → Размер → Где ставим (cond:built_in) → Особенности
- dw: Тип встройки → Класс (multi) → Ширина → Корзины → Особенности
- hood: Форм-фактор → Подключение → Ширина → Цвет (cond:visible-types) → Особенности
- microwave: Установка → Функции (multi) → Размер (optionsBy) → Особенности
- coffee: Тип → Молоко (cond:grinder/manual) → Вода (cond:built-in/tap) → Размер (cond:built-in) → Особенности
- washer: Установка → Функция → Глубина → Загрузка → Объём → Особенности

NEW PODBOR.JS FEATURES:
- isStepActive(step, answers) — predicate for condition field
- findNextActiveIdx / findPrevActiveIdx — skip inactive steps in navigation
- Auto-advance through inactive on single-select pick
- Review screen filters inactive steps
- isCategoryFilled checks only active single-steps
- buildPerCatSummary skips inactive
- Clearing dependent answers when condition's parent changes (in addition to optionsBy)

NEXT: pictograms for step 1 of each category (currently text-pin layout)
2026-05-11 11:28:50 +03:00
wasrusgen
dd400b71ac miniapp: new pricing flow — brand strategy + budget presets + multi pick strategy
NEW STRUCTURE:
- Step 4 'Бренд' — ai/single/different + brand picker or per-cat chips (now 4-state with 'avoid')
- Step 5 'Бюджет' — Люкс/Премиум/Средний/Бюджет/Точные цифры presets
- Step 6 'Стратегия' — multi: Лучшее по отзывам / Цена-качество / Топ-бренды / Доступное / Tech / Стиль
- Step 7 'Инфра' — перенесено после стратегии
- Step 8 'Итог' — обновлённый summary с новыми полями

FIXES:
- Keyboard-disappearing bug in price inputs — removed render() on input, total recomputed locally
- localStorage merge with defaults for backward compat with new fields
- Bumped STORAGE_KEY to v4

REMAINING:
- Backend still reads checklist.priorities (old shape) — needs update to read pick_strategies + brand_strategy + budget_preset
2026-05-11 10:43:54 +03:00
wasrusgen
496ddf793c miniapp: persistent category strip with active highlight + tap-to-jump
- Visible on all steps after categories are selected
- Highlights current category when inside its wizard
- Filled categories show checkmark
- Tap chip jumps directly to that category's wizard
- Horizontal scroll if many categories don't fit
2026-05-11 00:46:43 +03:00
wasrusgen
d289f7601e miniapp: compact pin layout for wizard steps without pictograms
- Steps with pict (1-3 fridge: install/chamber/size) keep grid cards
- Steps without pict (4 features) render as flex-wrap pill pins
- Auto-detect via options.some(o => o.pict)
2026-05-11 00:02:25 +03:00
wasrusgen
17b112f061 miniapp: hierarchical wizard for fridge category (style D pictograms)
- New PODBOR_PARAMS schema with steps[] supporting single/multi + optionsBy branches
- 11 fridge SVG pictograms in podbor.picts.js (style D — 3D perspective with shadow)
- renderCategoryWizard with step-by-step flow, chips for prior answers, review screen
- Legacy renderCategoryDetail still used for other 7 categories until migrated
- Auto-advance on single-select, Дальше button for multi-select
- Backend-compatible: per_cat[catKey].answers replaces .params/.features
2026-05-10 23:57:03 +03:00
wasrusgen
cd5d92ea17 miniapp: redraw fridge pictograms in style D (3D perspective with shadow) 2026-05-10 23:33:07 +03:00
wasrusgen
8991e7890d miniapp: add fridge style preview — 4 styles × 4 variants for comparison 2026-05-10 23:26:30 +03:00
wasrusgen
fe51f44bd9 fix(preview): correct fridge taxonomy — columns moved to built-in (3 variants: cold, freeze, pair); morozilka stays in freestanding 2026-05-10 23:16:43 +03:00
wasrusgen
f2e3333846 feat(miniapp/preview): SVG pictogram preview for fridge types (Editorial Calm, line-art, walnut) 2026-05-10 22:56:36 +03:00
wasrusgen
b7fa20dc69 fix(backend/sheets): write ISO-string for datetime (gspread can't serialize datetime) 2026-05-10 22:28:13 +03:00
wasrusgen
a849491f56 fix(miniapp): /api/me path for FastAPI backend (was ?path=me from Apps Script) 2026-05-10 22:19:34 +03:00
wasrusgen
f85d3a9d1e feat(miniapp): switch BACKEND_URL to Cloudflare Tunnel → VPS backend (GigaChat live) 2026-05-10 22:18:53 +03:00
wasrusgen
f7eb83634b feat(deploy): add Cloudflare Quick Tunnel service for public HTTPS while domain is on verification-hold 2026-05-10 22:16:07 +03:00
wasrusgen
5263840582 fix(backend): retry sheet open if cached _book is None (after PermissionError) 2026-05-10 21:06:02 +03:00
wasrusgen
0e5895bdc4 feat(infra): Python FastAPI backend + Docker compose for VPS deploy (GigaChat with Russian root CA) 2026-05-10 17:44:21 +03:00
wasrusgen
01aa47773e feat(backend): switch AI provider from Anthropic to GigaChat (Sber) — OAuth token caching, callAI dispatch 2026-05-10 14:22:53 +03:00
wasrusgen
5ecea8fd82 fix(backend): default model claude-haiku-4-5 (no date) + surface Anthropic error message 2026-05-10 10:17:20 +03:00
wasrusgen
cc28984122 feat(podbor iter2): per-category detail menu with primary params + accordion 'Подробнее' for tech features; updated AI prompt to require feature explanations 2026-05-09 15:21:05 +03:00
wasrusgen
129046de07 feat(podbor): drop niches; price-range from-to per cat; ventilation Y/N; priorities multi-select; brand tiers as color (no labels) 2026-05-09 15:10:23 +03:00
wasrusgen
571297c017 fix(podbor): niche inputs overflow — min-width:0 + shorter placeholders 2026-05-09 13:43:17 +03:00
wasrusgen
d1165d5e4f feat(miniapp): «Подбор техники» screen — 7-step picker (categories/niches/budget/infra/scenario/brands/summary) wired to /api/podbor 2026-05-09 13:34:46 +03:00
wasrusgen
86cd4eb614 fix(miniapp/A): tighter quick action cards (no min-height) + smaller hero buttons 2026-05-09 13:06:54 +03:00
wasrusgen
af7dc07720 feat: one-tap role buttons (WebApp directly, no intermediate step) + role param in URL/backend 2026-05-09 13:05:20 +03:00
wasrusgen
017d179746 feat(miniapp): manager home v2 — greeting + hero today-task + 2x2 quick actions + active projects + bottom nav 2026-05-09 12:59:41 +03:00
wasrusgen
435ef6817b feat(miniapp/A): rounded corners (16/12/6) + tighter rows (48px) 2026-05-09 12:34:49 +03:00
wasrusgen
ce91c0283b feat(miniapp): lock to variant A; green active dot; tighter spacing in menu and profile card 2026-05-09 12:25:19 +03:00
wasrusgen
7e0d2b98b0 feat(miniapp): three-variant design switcher (Brand/A/C) with literal palettes from mockups 2026-05-09 12:19:14 +03:00
wasrusgen
5032b27049 fix(miniapp): detect Telegram dark theme via tg.colorScheme; bump dark-mode contrast 2026-05-09 11:47:28 +03:00
wasrusgen
d7bd0aa5c2 feat(miniapp): hybrid Architectural Clean design — Inter + Instrument Serif italic + JetBrains Mono, paper palette, ZOV accents 2026-05-09 11:31:30 +03:00
wasrusgen
6fadc11163 fix(miniapp): bust browser/Telegram cache via versioned asset URLs + no-cache headers 2026-05-09 11:17:20 +03:00
wasrusgen
67dd0eac0c fix(miniapp): correct backend URL format + drop Content-Type to avoid CORS preflight 2026-05-09 11:11:21 +03:00
wasrusgen
4f0c51c453 feat(miniapp): connect to deployed Apps Script backend 2026-05-09 10:45:35 +03:00
wasrusgen
d835cac27f feat(backend): auto-seed admin on first /api/me + GET test endpoints 2026-05-09 10:37:28 +03:00