Explorer
/opt/struktur/lead-engine/lead_engine_v2.py
← Zurück ↓ Download
from __future__ import annotations

import argparse
import sqlite3
from pathlib import Path

from v2_schema import DB_PATH, init_schema, sync_projects
from lead_scoring_v2 import score_campaign


def list_projects(db_path: Path = DB_PATH) -> None:
    with sqlite3.connect(db_path) as conn:
        rows = conn.execute(
            'SELECT project_id,title,potential_score,confidence FROM product_catalog ORDER BY potential_score DESC'
        ).fetchall()
    for row in rows:
        print(f'{row[0]:28s} | {row[1]:42s} | {row[2] or 0:>4} | {row[3]}')


def shortlist(campaign_id: int, project_id: str | None, min_score: float, db_path: Path = DB_PATH) -> None:
    query = '''
      SELECT m.id,c.company,c.email,p.title,m.lead_score,m.human_gate,m.rationale
      FROM lead_project_matches m
      JOIN cold_contacts c ON c.id=m.contact_id
      JOIN product_catalog p ON p.project_id=m.project_id
      WHERE c.campaign_id=? AND m.lead_score>=?
    '''
    params = [campaign_id, min_score]
    if project_id:
        query += ' AND m.project_id=?'
        params.append(project_id)
    query += ' ORDER BY m.lead_score DESC LIMIT 100'
    with sqlite3.connect(db_path) as conn:
        rows = conn.execute(query, params).fetchall()
    for r in rows:
        print(f'[{r[0]}] {r[1]} | {r[2]} | {r[3]} | Score {r[4]:.2f} | Gate {r[5]}')
        print(f'    {r[6]}')


def set_gate(match_id: int, decision: str, db_path: Path = DB_PATH) -> None:
    if decision not in {'approved', 'rejected', 'pending'}:
        raise ValueError('decision must be approved, rejected or pending')
    with sqlite3.connect(db_path) as conn:
        conn.execute(
            'UPDATE lead_project_matches SET human_gate=?, updated_at=CURRENT_TIMESTAMP WHERE id=?',
            (decision, match_id),
        )
        conn.commit()
        row = conn.execute(
            'SELECT id,human_gate,project_id,contact_id FROM lead_project_matches WHERE id=?',
            (match_id,),
        ).fetchone()
    if not row:
        raise SystemExit(f'Match {match_id} not found')
    print(f'match_id={row[0]} gate={row[1]} project={row[2]} contact={row[3]}')
def main() -> None:
    parser = argparse.ArgumentParser(description='Agent Solutions Lead Engine 2.0')
    sub = parser.add_subparsers(dest='cmd', required=True)
    sub.add_parser('sync-projects')
    sub.add_parser('projects')

    score = sub.add_parser('score')
    score.add_argument('--campaign', type=int, required=True)

    short = sub.add_parser('shortlist')
    short.add_argument('--campaign', type=int, required=True)
    short.add_argument('--project')
    short.add_argument('--min-score', type=float, default=7.0)

    gate = sub.add_parser('gate')
    gate.add_argument('--match', type=int, required=True)
    gate.add_argument('--decision', choices=['approved','rejected','pending'], required=True)

    args = parser.parse_args()
    init_schema()
    if args.cmd == 'sync-projects':
        print(f'synced_projects={sync_projects()}')
    elif args.cmd == 'projects':
        sync_projects(); list_projects()
    elif args.cmd == 'score':
        print(f'scored_matches={score_campaign(args.campaign)}')
    elif args.cmd == 'shortlist':
        shortlist(args.campaign, args.project, args.min_score)
    elif args.cmd == 'gate':
        set_gate(args.match, args.decision)


if __name__ == '__main__':
    main()