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()