-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
134 lines (115 loc) · 5.99 KB
/
Copy pathmain.py
File metadata and controls
134 lines (115 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python3
"""
Generic CLI entrypoint for the keyword-based and semantic persona filter.
Parses arguments and orchestrates filtering runs.
"""
import argparse
import csv
import json
from pathlib import Path
from typing import List, Dict, Any
import logging
from src.keywords_filter import KeywordsFilter
from semantic_filter import find_best_fit
def run_keyword_filter(args):
"""Runs the keyword-based filtering process."""
try:
kf = KeywordsFilter(args.config)
if args.require_org:
kf.require_org = True
if not args.personas:
personas = [kf.active_persona]
else:
if args.personas.lower() == 'all':
personas = list(kf.config['personas'].keys())
else:
personas = [p.strip() for p in args.personas.split(',') if p.strip()]
missing = [p for p in personas if p not in kf.config['personas']]
if missing:
print(f"Persona(s) not found: {', '.join(missing)}")
return 1
input_paths = [Path(p) for p in args.input]
for path in input_paths:
if not path.exists():
print(f"Input file not found: {path}")
return 1
output_dir = Path(args.output) if args.output else Path(kf.config['output']['directory'])
output_dir.mkdir(exist_ok=True)
combined_results: List[Dict[str, Any]] = []
combined_stats: Dict[str, Any] = {}
for persona in personas:
kf.active_persona = persona
kf.persona_config = kf.config['personas'][persona]
for input_path in input_paths:
results, stats = kf.process(str(input_path))
kf.save(results, stats, output_dir)
if not args.quiet:
kf.print_summary(results, stats)
combined_results.extend(results)
if combined_stats.get('total_rows') is None:
combined_stats['total_rows'] = 0
combined_stats['total_rows'] += stats['total_rows']
combined_stats['final_matches'] = combined_stats.get('final_matches', 0) + stats['final_matches']
if args.save_combined and len(personas) > 1 and combined_results:
combined_csv = output_dir / 'combined_filtered_connections.csv'
with open(combined_csv, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=combined_results[0].keys())
writer.writeheader()
writer.writerows(combined_results)
combined_json = output_dir / 'combined_analysis_summary.json'
with open(combined_json, 'w', encoding='utf-8') as f:
json.dump({'personas_run': personas, 'statistics': combined_stats, 'require_org': kf.require_org}, f, indent=2, ensure_ascii=False)
if not args.quiet:
print(f"Combined results: {combined_csv}\nCombined summary: {combined_json}")
return 0
except Exception as e:
print(f"Error: {e}")
if args.debug:
import traceback; traceback.print_exc()
return 1
def run_semantic_filter(args):
"""Runs the semantic filtering process."""
try:
find_best_fit(args.persona, args.model, args.input, args.config, args.output)
return 0
except Exception as e:
print(f"Error: {e}")
if args.debug:
import traceback; traceback.print_exc()
return 1
def main():
parser = argparse.ArgumentParser(
description='Persona filter for LinkedIn connections.',
formatter_class=argparse.RawDescriptionHelpFormatter
)
subparsers = parser.add_subparsers(dest='command', required=True)
# Keyword filter subcommand
parser_keyword = subparsers.add_parser('keyword', help='Keyword-based persona filtering.')
parser_keyword.add_argument('--input', '-i', default=['Connections.csv'], nargs='+', help='Input CSV(s) (LinkedIn export)')
parser_keyword.add_argument('--config', '-c', default='config.yaml', help='Configuration YAML file')
parser_keyword.add_argument('--output', '-o', help='Output directory (default: from config)')
parser_keyword.add_argument('--personas', '-p', help='Persona name, comma-separated list, or "all" (default: active_persona)')
parser_keyword.add_argument('--require-org', action='store_true', help='Require organization match for final inclusion')
parser_keyword.add_argument('--save-combined', action='store_true', help='When running multiple personas, save combined CSV/JSON')
parser_keyword.add_argument('--debug', '-d', action='store_true', help='Enable debug logging')
parser_keyword.add_argument('--quiet', '-q', action='store_true', help='Quiet mode - minimal output')
parser_keyword.set_defaults(func=run_keyword_filter)
# Semantic filter subcommand
parser_sbert = subparsers.add_parser('sbert', help='Semantic profile filtering with Sentence-BERT.')
parser_sbert.add_argument("--persona", required=True, help="The name of the persona to use for matching.")
parser_sbert.add_argument("--model", default="paraphrase-multilingual-MiniLM-L12-v2", help="The Sentence-BERT model to use.")
parser_sbert.add_argument("--input", default="apollo_people_data.csv", help="The input CSV file with enriched profiles.")
parser_sbert.add_argument("--config", default="config.yaml", help="The configuration YAML file.")
parser_sbert.add_argument("--output", help="The output CSV file path.")
parser_sbert.add_argument('--debug', '-d', action='store_true', help='Enable debug logging')
parser_sbert.set_defaults(func=run_semantic_filter)
args = parser.parse_args()
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
elif hasattr(args, 'quiet') and args.quiet:
logging.getLogger().setLevel(logging.WARNING)
else:
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
return args.func(args)
if __name__ == '__main__':
exit(main())