Add monitoring PF service

This commit is contained in:
Grendgi
2026-06-04 14:55:41 +03:00
commit dd3edd7088
41 changed files with 3194 additions and 0 deletions

90
SESSION_NOTES.md Normal file
View File

@@ -0,0 +1,90 @@
# Журнал сессии (2026-05-25)
## Что построили
Внутренний мониторинг цен конкурентов на propertyfinder.ae и bayut.com для HOME LIGA REAL ESTATE.
**Стек:** FastAPI + Jinja2 (Bootstrap 5) + SQLAlchemy/SQLite + APScheduler + python-telegram-bot + httpx/BS4.
**Три процесса** (каждый — свой .bat-лаунчер):
- `run_web.bat` — веб-UI на http://127.0.0.1:8000
- `run_bot.bat` — Telegram-бот (polling)
- `run_scheduler.bat` — фоновый сканер каждые `SCRAPE_INTERVAL_HOURS` часов
## Эволюция архитектуры
### Первая попытка (отвергнута)
**Идея:** сотрудник вводит DLD Permit Number своего объекта → система автоматически ищет «то же permit» на PF и Bayut → находит объявления конкурентов.
**Почему не сработало:**
1. В Дубае **каждый брокер получает свой permit на свою публикацию**. Два брокера, рекламирующих одну квартиру = два разных permit. Permit не идентифицирует физический объект — он идентифицирует конкретное объявление конкретного брокера.
2. PF на странице объявления показывает permit **картинкой** через сервис верификации, не plain text (anti-scraping).
3. PF search `?q=<permit>` — free-text по названию/описанию, не структурированный фильтр.
### Финальная архитектура
**Manual URL list + опциональные подсказки:**
1. Сотрудник создаёт проект (название, тип сделки, владелец, опц. building/bedrooms/sqft).
2. На странице проекта **вручную вставляет URL** объявления конкурента → система делает single-page fetch, парсит `__NEXT_DATA__`, добавляет в трекинг.
3. Если указано здание — кнопка «🔍 Подобрать похожие» ищет на PF/Bayut по `building + bedrooms` и предлагает кандидатов с кнопкой «+ Отслеживать».
4. Каждые 4 часа фоновый сканер делает refetch каждого отслеживаемого URL → детектит:
- 📈📉 изменение цены
- ❌ удаление (URL отдаёт 404)
- ♻️ возвращение из удалённого статуса
5. Уведомления — в Telegram личкой владельцу проекта.
## Модель данных
- `Employee` — name, tg_chat_id (опц.), tg_username
- `Project` — title, deal_type, owner_id, our_price, building, bedrooms, size_sqft, our_url, dld_permit (все после `owner` — опционально)
- `CompetitorListing` — source (PF|Bayut), external_id, url, current_price, status (active|removed), agent_name, agency_name, first_seen, last_seen
- `PriceHistory` — listing_id, price, recorded_at
## Как подключиться сотруднику
1. В TG найти бота → отправить `/start`.
2. Отправить `/whoami` → бот пришлёт chat_id.
3. В вебе http://127.0.0.1:8000/employees → найти/создать запись → вставить chat_id → Сохранить.
## Известные ограничения
- **PF/Bayut могут блокировать** при частых запросах (видно как `Blocked by site (403/429)` в логах). Решение — увеличить интервал; если уже не помогает — добавить Playwright fallback.
- **Подсказки эвристические**: ищем по совпадению building name в title + bedrooms-фильтр. Могут попасть «другие квартиры в том же здании» — поэтому добавление в трекинг через ручное подтверждение.
- **Permit как plain text** на PF не отдаётся. Если когда-нибудь понадобится — нужен OCR на verification-image.
## Что в .env
```
TG_BOT_TOKEN=<токен от @BotFather>
SCRAPE_INTERVAL_HOURS=4
ADMIN_CHAT_ID= # опц.
```
## Структура проекта
```
DLD Permit Number/
├── run_web.bat, run_bot.bat, run_scheduler.bat
├── run_web.py
├── requirements.txt
├── .env (не в git — содержит токен)
├── data/monitor.db (создаётся автоматически)
├── app/
│ ├── config.py ← settings + резолвит относительные SQLite-пути в абсолютные
│ ├── db.py, models.py
│ ├── web.py ← FastAPI: CRUD проектов, /listings add/delete, /suggest
│ ├── bot.py ← /start, /whoami, /list, /check
│ ├── scheduler.py
│ ├── scrapers/{base,propertyfinder,bayut}.py
│ ├── services/{monitor,notifier}.py
│ └── templates/ ← projects_list, project_form, project_detail, suggest, employees, base
```
## Что протестировать в первую очередь
1. Удалить старый `data/monitor.db` (схема изменилась).
2. Запустить три .bat файла.
3. `/start` боту → `/whoami` → chat_id → вписать в Сотрудники.
4. Создать тестовый проект (Aykon City Tower B, 2BR).
5. Вставить URL реального объявления конкурента → проверить, что добавилось с ценой/брокером.
6. Жмякнуть «Подобрать похожие» → посмотреть кандидатов.
7. Жмякнуть «Проверить сейчас» → если цена не менялась, изменений не будет (это нормально); для теста алертов можно поменять цену в БД руками или подождать реального изменения.