feat: Admin-панель в CRM + CLI --repass / --delete-db
- app/app.py: навигация CRM / Управление (st.sidebar.radio), render_admin() со статистикой, кнопками rescore / repass / сброс флагов / удаление БД с чекбоксом - main.py: run_repass() (ренормализация телефонов+доменов + rescore, без HTTP), флаги --delete-db и --repass, Path import добавлен Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,7 @@ import random
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import config
|
||||
from database import (
|
||||
@@ -691,6 +692,54 @@ def run_rescore(conn) -> int:
|
||||
return changed
|
||||
|
||||
|
||||
def run_repass(conn) -> dict:
|
||||
"""Ренормализация телефонов/доменов + rescore. Без HTTP-запросов."""
|
||||
from normalization import normalize_phone, normalize_domain
|
||||
|
||||
leads = get_all_leads(conn)
|
||||
phones_fixed = 0
|
||||
domains_fixed = 0
|
||||
|
||||
for row in leads:
|
||||
lead = dict(row)
|
||||
updates = {}
|
||||
|
||||
# Нормализация phone_primary
|
||||
raw_phone = lead.get("phone_primary")
|
||||
if raw_phone and not raw_phone.startswith("+"):
|
||||
normed = normalize_phone(raw_phone)
|
||||
if normed and normed != raw_phone:
|
||||
updates["phone_primary"] = normed
|
||||
phones_fixed += 1
|
||||
|
||||
# Починить website без протокола
|
||||
raw_site = lead.get("website")
|
||||
if raw_site and not raw_site.startswith(("http://", "https://")):
|
||||
updates["website"] = "https://" + raw_site.lstrip("/")
|
||||
domains_fixed += 1
|
||||
|
||||
if updates:
|
||||
sets = ", ".join(f"{k} = ?" for k in updates)
|
||||
vals = list(updates.values()) + [lead["id"]]
|
||||
conn.execute(f"UPDATE leads SET {sets} WHERE id = ?", vals)
|
||||
|
||||
if phones_fixed or domains_fixed:
|
||||
conn.commit()
|
||||
|
||||
scores_changed = run_rescore(conn)
|
||||
|
||||
logger.info(
|
||||
f"repass: телефонов нормализовано={phones_fixed}, "
|
||||
f"доменов исправлено={domains_fixed}, score изменился у {scores_changed}"
|
||||
)
|
||||
return {
|
||||
"phones_fixed": phones_fixed,
|
||||
"domains_fixed": domains_fixed,
|
||||
"scores_changed": scores_changed,
|
||||
"total": len(leads),
|
||||
}
|
||||
|
||||
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
# CLI
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
@@ -770,6 +819,17 @@ def main():
|
||||
action="store_true",
|
||||
help="Пересчитать score у всех лидов (после изменения формулы)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--delete-db",
|
||||
action="store_true",
|
||||
dest="delete_db",
|
||||
help="Удалить leads.db + WAL/SHM файлы и пересоздать пустую схему",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--repass",
|
||||
action="store_true",
|
||||
help="Ренормализация телефонов/доменов + rescore без HTTP-запросов",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--full",
|
||||
action="store_true",
|
||||
@@ -910,10 +970,29 @@ def main():
|
||||
args.city = known
|
||||
break
|
||||
|
||||
# --delete-db: обработать до открытия соединения
|
||||
if args.delete_db:
|
||||
db_path = Path(config.DB_PATH)
|
||||
for suffix in ("", "-wal", "-shm"):
|
||||
p = Path(str(db_path) + suffix) if suffix else db_path
|
||||
if p.exists():
|
||||
p.unlink()
|
||||
logger.info(f"Удалён: {p}")
|
||||
init_db(str(config.DB_PATH))
|
||||
logger.info("БД пересоздана (пустая схема)")
|
||||
return
|
||||
|
||||
# Инициализация БД
|
||||
init_db(config.DB_PATH)
|
||||
conn = get_connection(config.DB_PATH)
|
||||
try:
|
||||
if args.repass:
|
||||
result = run_repass(conn)
|
||||
logger.info(
|
||||
f"repass завершён: телефонов={result['phones_fixed']}, "
|
||||
f"доменов={result['domains_fixed']}, score={result['scores_changed']}/{result['total']}"
|
||||
)
|
||||
|
||||
# Только статистика — выходим сразу
|
||||
if args.stats:
|
||||
stats = get_stats(conn)
|
||||
@@ -956,6 +1035,8 @@ def main():
|
||||
or args.find_sites
|
||||
or args.export_master
|
||||
or args.export_run is not None
|
||||
or args.delete_db
|
||||
or args.repass
|
||||
):
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user