[HRT-66] LeadHunter S1 — Core scraping, scoring, CRM SQLite et API Flask #8

Merged
admin merged 3 commits from feature/HRT-66-leadhunter-core into master 2026-04-27 16:55:02 +02:00
Owner

Résumé

Implémentation S1 du projet LeadHunter — agent de détection de restaurants sans site web dans la MEL (Métropole Européenne de Lille).

Issue Paperclip : HRT-66


Fichiers livrés

leadhunter_scraper.py

  • Google Places Nearby Search + Place Details
  • Compteur quota journalier (/home/h3r7/leadhunter_quota.json, limite 900/jour)
  • time.sleep(0.5) entre requêtes Places API
  • Fallback Overpass OSM avec boundary nommée Métropole Européenne de Lille
  • Filtre website: absent appliqué côté API et côté scraper
  • Déduplication par name+address
  • rgpd_ok: True (données publiques Google Places + ODbL)

leadhunter_scorer.py

  • Score 0–8 pts par lead
  • Critère n°1 : site web absent → +3 (logique métier centrale)
  • Avis ≥ 50 → +2 | Note ≥ 4.0 → +2 | Téléphone → +1 | Note < 3.0 → -1
  • Tri par score décroissant

leadhunter_crm.py

  • SQLite sur /home/h3r7/leadhunter.db
  • Schéma validé CTO : id, source, name, address, phone, rating, reviews_count, website, score, rgpd_ok, scraped_at, status
  • CRUD complet : insert_lead, get_leads, update_lead_status, get_stats, export_csv
  • Logging RotatingFileHandler/home/h3r7/leadhunter.log

leadhunter_api.py

  • Flask service port 8769
  • assert GOOGLE_PLACES_API_KEY au démarrage
  • Endpoints :
    • GET /api/leads — liste avec filtres status/limit/offset
    • POST /api/leads/scrape — lance scraping asynchrone (thread)
    • GET /api/leads/scrape/status — état du job
    • GET /api/leads/stats — statistiques CRM
    • GET /api/leads/export — export CSV
    • PATCH /api/leads/<id>/status — mise à jour statut
    • GET /health — healthcheck

infra/turf-saas-leadhunter.service

  • Service systemd : user h3r7, WorkingDirectory turf_saas
  • EnvironmentFile=/home/h3r7/.env pour charger GOOGLE_PLACES_API_KEY

Tests réalisés

  • python3 -m py_compile : tous les fichiers OK
  • Scorer : testé sur 4 leads, scores attendus (8/3/3/0)
  • CRM : insert, get, update_status, export_csv testés sur DB temporaire

Notes déploiement

  1. Ajouter GOOGLE_PLACES_API_KEY=xxx dans /home/h3r7/.env
  2. sudo cp infra/turf-saas-leadhunter.service /etc/systemd/system/
  3. sudo systemctl daemon-reload && sudo systemctl enable --now turf-saas-leadhunter
  4. Proxy nginx/portal vers port 8769 si besoin (reverse proxy validé CTO séparément)

Reviewers : @admin (CTO)

## Résumé Implémentation S1 du projet LeadHunter — agent de détection de restaurants sans site web dans la MEL (Métropole Européenne de Lille). **Issue Paperclip** : HRT-66 --- ## Fichiers livrés ### `leadhunter_scraper.py` - Google Places Nearby Search + Place Details - Compteur quota journalier (`/home/h3r7/leadhunter_quota.json`, limite 900/jour) - `time.sleep(0.5)` entre requêtes Places API - Fallback Overpass OSM avec boundary nommée `Métropole Européenne de Lille` - Filtre `website: absent` appliqué côté API et côté scraper - Déduplication par `name+address` - `rgpd_ok: True` (données publiques Google Places + ODbL) ### `leadhunter_scorer.py` - Score 0–8 pts par lead - Critère n°1 : site web absent → +3 (logique métier centrale) - Avis ≥ 50 → +2 | Note ≥ 4.0 → +2 | Téléphone → +1 | Note < 3.0 → -1 - Tri par score décroissant ### `leadhunter_crm.py` - SQLite sur `/home/h3r7/leadhunter.db` - Schéma validé CTO : `id, source, name, address, phone, rating, reviews_count, website, score, rgpd_ok, scraped_at, status` - CRUD complet : `insert_lead`, `get_leads`, `update_lead_status`, `get_stats`, `export_csv` - Logging `RotatingFileHandler` → `/home/h3r7/leadhunter.log` ### `leadhunter_api.py` - Flask service port **8769** - `assert GOOGLE_PLACES_API_KEY` au démarrage - Endpoints : - `GET /api/leads` — liste avec filtres status/limit/offset - `POST /api/leads/scrape` — lance scraping asynchrone (thread) - `GET /api/leads/scrape/status` — état du job - `GET /api/leads/stats` — statistiques CRM - `GET /api/leads/export` — export CSV - `PATCH /api/leads/<id>/status` — mise à jour statut - `GET /health` — healthcheck ### `infra/turf-saas-leadhunter.service` - Service systemd : user h3r7, WorkingDirectory turf_saas - `EnvironmentFile=/home/h3r7/.env` pour charger `GOOGLE_PLACES_API_KEY` --- ## Tests réalisés - `python3 -m py_compile` : tous les fichiers OK - Scorer : testé sur 4 leads, scores attendus (8/3/3/0) - CRM : insert, get, update_status, export_csv testés sur DB temporaire --- ## Notes déploiement 1. Ajouter `GOOGLE_PLACES_API_KEY=xxx` dans `/home/h3r7/.env` 2. `sudo cp infra/turf-saas-leadhunter.service /etc/systemd/system/` 3. `sudo systemctl daemon-reload && sudo systemctl enable --now turf-saas-leadhunter` 4. Proxy nginx/portal vers port 8769 si besoin (reverse proxy validé CTO séparément) --- **Reviewers** : @admin (CTO)
admin added 1 commit 2026-04-27 16:33:57 +02:00
- leadhunter_scraper.py : Google Places Nearby Search + Place Details
  avec compteur quota daily_quota.json (limite 900/jour),
  sleep(0.5) entre requêtes, fallback Overpass OSM boundary MEL,
  filtre website absent, déduplcation, rgpd_ok=True

- leadhunter_scorer.py : moteur de scoring 0-8 pts
  critère n°1 = site web absent (+3), avis ≥50 (+2),
  note ≥4.0 (+2), téléphone (+1), note <3.0 (-1)

- leadhunter_crm.py : CRM SQLite schéma validé CTO
  (id, source, name, address, phone, rating, reviews_count,
   website, score, rgpd_ok, scraped_at, status)
  CRUD : insert_lead, get_leads, update_lead_status, get_stats, export_csv

- leadhunter_api.py : Flask service port 8769
  GET /api/leads, POST /api/leads/scrape, GET /api/leads/stats,
  GET /api/leads/export, PATCH /api/leads/<id>/status, GET /health
  assert GOOGLE_PLACES_API_KEY au démarrage
  scraping asynchrone (thread) avec status endpoint

- infra/turf-saas-leadhunter.service : service systemd
  EnvironmentFile=/home/h3r7/.env pour GOOGLE_PLACES_API_KEY

Tests : py_compile OK, scorer testé, CRM SQLite testé

Co-Authored-By: Paperclip <noreply@paperclip.ing>
admin added 1 commit 2026-04-27 16:42:21 +02:00
Port 8769 était occupé par /home/h3r7/depenses_trello/app.py (pid=2287989).
Mise à jour du port dans :
- leadhunter_api.py (docstring, healthcheck, app.run)
- infra/turf-saas-leadhunter.service (description)

Ref: HRT-66

Co-Authored-By: Paperclip <noreply@paperclip.ing>
admin added 1 commit 2026-04-27 16:48:19 +02:00
Port audit sur VPS (27/04/2026) :
- 8769 : depenses_trello/app.py (PID 2287989)
- 8770 : turf_scraper/crm_api.py (PID 2287988) ← port précédemment choisi, aussi occupé
- 8775 : libre (vérifié via ss -tlnp | grep 8775 → vide)

Fichiers modifiés :
- leadhunter_api.py : lignes 5, 295, 303 (port 8770→8775)
- infra/turf-saas-leadhunter.service : Description Port 8770→8775

Issue: HRT-66

Co-Authored-By: Paperclip <noreply@paperclip.ing>
admin merged commit 5aa6013c52 into master 2026-04-27 16:55:02 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/turf_saas#8