2121from five08 .job_leads import (
2222 JobLeadInput ,
2323 existing_job_lead_external_ids ,
24+ reject_unreviewed_irrelevant_job_leads ,
2425 update_existing_job_lead ,
2526 upsert_job_lead ,
2627)
5960 r"operations|solutions|compliance|officer)\b" ,
6061 re .I ,
6162)
63+ _COOP_ROLE_RE = re .compile (
64+ r"\b(?:"
65+ r"(?:software|full[ -]?stack|front[ -]?end|back[ -]?end|web|mobile|"
66+ r"platform|infrastructure|cloud|devops|site reliability|security|"
67+ r"machine learning|ml|ai|data|analytics|mechanical|electrical|"
68+ r"systems?)\s+(?:engineer(?:ing)?|developer|architect)|"
69+ r"engineer(?:ing)?|developer|programmer|sre|devops|"
70+ r"full[ -]?stack|front[ -]?end|back[ -]?end|data science|"
71+ r"data\s+(?:scientist|analyst|architect|specialist)|"
72+ r"(?:product|technical product)\s+(?:manager|designer|lead|owner|analyst)|"
73+ r"(?:ux|ui|ux/ui|graphic|graphics|visual|brand|web|creative)\s+designer|"
74+ r"(?:ux|ui|ux/ui)\s+researcher|"
75+ r"(?:digital|performance|growth|product|content|email|social media)?\s*"
76+ r"marketing\s+(?:expert|specialist|manager|strategist|consultant|lead)|"
77+ r"(?:seo|sem|paid media|media buying)\s+"
78+ r"(?:expert|specialist|manager|strategist|consultant|lead)|"
79+ r"media\s+(?:buyer|planner)|"
80+ r"(?:shopify|e[ -]?commerce|amazon marketplace)\s+"
81+ r"(?:expert|specialist|developer|designer|consultant|manager|lead)|"
82+ r"(?:tech|technical|engineering)\s+(?:lead|architect)|"
83+ r"(?:database|db|business intelligence|bi)\s+(?:administrator|analyst|engineer)"
84+ r")\b" ,
85+ re .I ,
86+ )
87+ _NON_COOP_ROLE_RE = re .compile (
88+ r"\b(?:recruit(?:er|ing|ment)|talent acquisition|sourc(?:er|ing)|"
89+ r"sales|account manager|copywriter|transcri(?:ber|ption)|"
90+ r"data (?:entry|rater|annotator)|ai trainer|"
91+ r"nurs(?:e|ing)|physician|therapist|psychologist|counselor)\b" ,
92+ re .I ,
93+ )
94+
95+
96+ def _title_is_coop_role (title : str ) -> bool :
97+ """Keep explicit technical roles; source descriptions often mention unrelated skills."""
98+ return any (
99+ _COOP_ROLE_RE .search (part ) and not _NON_COOP_ROLE_RE .search (part )
100+ for part in re .split (r"[|,;]" , title )
101+ )
102+
103+
104+ def _lead_is_coop_role (source_key : str , title : str , body : str ) -> bool :
105+ """HN's full first line can carry roles beyond its abbreviated display title."""
106+ if source_key == HN_WHO_IS_HIRING_SOURCE_KEY and body :
107+ return _title_is_coop_role (body .splitlines ()[0 ])
108+ return _title_is_coop_role (title )
62109
63110
64111def _title_is_gig (title : str ) -> bool :
@@ -338,13 +385,15 @@ class HackerNewsThreadScrapeReport:
338385 filtered_empty : int = 0
339386 filtered_seeking_work : int = 0
340387 filtered_not_contractor_friendly : int = 0
388+ filtered_irrelevant_role : int = 0
341389
342390 @property
343391 def filtered_out (self ) -> int :
344392 return (
345393 self .filtered_empty
346394 + self .filtered_seeking_work
347395 + self .filtered_not_contractor_friendly
396+ + self .filtered_irrelevant_role
348397 )
349398
350399 def payload (self ) -> dict [str , Any ]:
@@ -362,6 +411,7 @@ def payload(self) -> dict[str, Any]:
362411 "empty" : self .filtered_empty ,
363412 "seeking_work" : self .filtered_seeking_work ,
364413 "not_contractor_friendly" : self .filtered_not_contractor_friendly ,
414+ "irrelevant_role" : self .filtered_irrelevant_role ,
365415 },
366416 }
367417
@@ -998,6 +1048,7 @@ def __init__(
9981048 self .include_latest = include_latest
9991049 self .include_non_contractor = include_non_contractor
10001050 self ._thread_reports : list [HackerNewsThreadScrapeReport ] = []
1051+ self .irrelevant_external_ids : set [str ] = set ()
10011052
10021053 def discover_threads (self ) -> list [HackerNewsThread ]:
10031054 if self .story_id is not None :
@@ -1016,6 +1067,7 @@ def discover_threads(self) -> list[HackerNewsThread]:
10161067 def collect (self ) -> list [JobLeadInput ]:
10171068 leads : list [JobLeadInput ] = []
10181069 self ._thread_reports = []
1070+ self .irrelevant_external_ids = set ()
10191071 for thread in self .discover_threads ():
10201072 tree = self .client .get_algolia_item_tree (thread .story_id )
10211073 children = tree .get ("children" ) or []
@@ -1043,6 +1095,12 @@ def collect(self) -> list[JobLeadInput]:
10431095 if lead is None :
10441096 report .filtered_not_contractor_friendly += 1
10451097 continue
1098+ if not _lead_is_coop_role (
1099+ self .source_key , lead .title , lead .body_normalized
1100+ ):
1101+ report .filtered_irrelevant_role += 1
1102+ self .irrelevant_external_ids .add (lead .external_id )
1103+ continue
10461104 classification = (lead .metadata or {}).get (
10471105 "contractor_classification" , {}
10481106 )
@@ -1065,6 +1123,9 @@ def collection_report(self) -> dict[str, Any]:
10651123 report .filtered_not_contractor_friendly
10661124 for report in self ._thread_reports
10671125 ),
1126+ "irrelevant_role" : sum (
1127+ report .filtered_irrelevant_role for report in self ._thread_reports
1128+ ),
10681129 }
10691130 return {
10701131 "thread_found" : bool (self ._thread_reports ),
@@ -1090,6 +1151,8 @@ def __init__(
10901151 self .max_pages = max_pages
10911152 self .scanned = 0
10921153 self .expired = 0
1154+ self .irrelevant_role = 0
1155+ self .irrelevant_external_ids : set [str ] = set ()
10931156 self .pages = 0
10941157 self .included = 0
10951158 self .incomplete = False
@@ -1098,7 +1161,12 @@ def __init__(
10981161 def collect (self ) -> list [JobLeadInput ]:
10991162 leads : list [JobLeadInput ] = []
11001163 seen : set [str ] = set ()
1101- self .scanned = self .expired = self .pages = self .included = 0
1164+ self .scanned = 0
1165+ self .expired = 0
1166+ self .irrelevant_role = 0
1167+ self .irrelevant_external_ids = set ()
1168+ self .pages = 0
1169+ self .included = 0
11021170 self .incomplete = False
11031171 self .incomplete_reason = None
11041172 now = datetime .now (timezone .utc )
@@ -1162,6 +1230,10 @@ def collect(self) -> list[JobLeadInput]:
11621230 ):
11631231 continue
11641232 seen .add (guid )
1233+ if not _title_is_coop_role (title ):
1234+ self .irrelevant_role += 1
1235+ self .irrelevant_external_ids .add (guid )
1236+ continue
11651237 body_raw = str (job .get ("description" ) or job .get ("excerpt" ) or "" )
11661238 body = html_to_text (body_raw )
11671239 posting_type = JobPostingType .PART_TIME
@@ -1226,7 +1298,11 @@ def collection_report(self) -> dict[str, Any]:
12261298 "filtered_out" : self .scanned - self .included ,
12271299 "filter_reasons" : {
12281300 "expired" : self .expired ,
1229- "invalid_or_duplicate" : self .scanned - self .included - self .expired ,
1301+ "irrelevant_role" : self .irrelevant_role ,
1302+ "invalid_or_duplicate" : self .scanned
1303+ - self .included
1304+ - self .expired
1305+ - self .irrelevant_role ,
12301306 },
12311307 "pages_scanned" : self .pages ,
12321308 "incomplete" : self .incomplete ,
@@ -1243,11 +1319,14 @@ def __init__(self, *, timeout_seconds: float = 15.0) -> None:
12431319 self .timeout_seconds = timeout_seconds
12441320 self .scanned = 0
12451321 self .included = 0
1322+ self .irrelevant_role = 0
1323+ self .irrelevant_external_ids : set [str ] = set ()
12461324
12471325 def collect (self ) -> list [JobLeadInput ]:
12481326 leads : list [JobLeadInput ] = []
12491327 seen : set [str ] = set ()
1250- self .scanned = self .included = 0
1328+ self .scanned = self .included = self .irrelevant_role = 0
1329+ self .irrelevant_external_ids = set ()
12511330 for term in A16Z_SEARCH_TERMS :
12521331 request = Request (
12531332 f"https://jobs.a16z.com/jobs?{ urlencode ({'q' : term })} " ,
@@ -1271,6 +1350,10 @@ def collect(self) -> list[JobLeadInput]:
12711350 path = title_links [0 ].get ("href" , "" )
12721351 if not path .startswith ("/jobs/" ) or not _title_is_gig (title ):
12731352 continue
1353+ if not _title_is_coop_role (title ):
1354+ self .irrelevant_role += 1
1355+ self .irrelevant_external_ids .add (f"https://jobs.a16z.com{ path } " )
1356+ continue
12741357 source_url = f"https://jobs.a16z.com{ path } "
12751358 if source_url in seen :
12761359 continue
@@ -1307,7 +1390,12 @@ def collection_report(self) -> dict[str, Any]:
13071390 "potential_gigs_scraped" : self .scanned ,
13081391 "included" : self .included ,
13091392 "filtered_out" : self .scanned - self .included ,
1310- "filter_reasons" : {"invalid_or_duplicate" : self .scanned - self .included },
1393+ "filter_reasons" : {
1394+ "irrelevant_role" : self .irrelevant_role ,
1395+ "invalid_or_duplicate" : self .scanned
1396+ - self .included
1397+ - self .irrelevant_role ,
1398+ },
13111399 "searches_scanned" : len (A16Z_SEARCH_TERMS ),
13121400 }
13131401
@@ -1321,10 +1409,13 @@ def __init__(self, *, timeout_seconds: float = 15.0) -> None:
13211409 self .timeout_seconds = timeout_seconds
13221410 self .scanned = 0
13231411 self .included = 0
1412+ self .irrelevant_role = 0
1413+ self .irrelevant_external_ids : set [str ] = set ()
13241414
13251415 def collect (self ) -> list [JobLeadInput ]:
13261416 leads : list [JobLeadInput ] = []
1327- self .scanned = self .included = 0
1417+ self .scanned = self .included = self .irrelevant_role = 0
1418+ self .irrelevant_external_ids = set ()
13281419 for company , board in SEQUOIA_GREENHOUSE_BOARDS .items ():
13291420 request = Request (
13301421 f"https://boards-api.greenhouse.io/v1/boards/{ board } /jobs" ,
@@ -1343,6 +1434,11 @@ def collect(self) -> list[JobLeadInput]:
13431434 source_url = job .get ("absolute_url" )
13441435 if not isinstance (title , str ) or not _title_is_gig (title ):
13451436 continue
1437+ if not _title_is_coop_role (title ):
1438+ self .irrelevant_role += 1
1439+ if isinstance (source_url , str ):
1440+ self .irrelevant_external_ids .add (source_url )
1441+ continue
13461442 if (
13471443 not isinstance (source_url , str )
13481444 or urlsplit (source_url ).scheme != "https"
@@ -1375,7 +1471,12 @@ def collection_report(self) -> dict[str, Any]:
13751471 "potential_gigs_scraped" : self .scanned ,
13761472 "included" : self .included ,
13771473 "filtered_out" : self .scanned - self .included ,
1378- "filter_reasons" : {"invalid_or_duplicate" : self .scanned - self .included },
1474+ "filter_reasons" : {
1475+ "irrelevant_role" : self .irrelevant_role ,
1476+ "invalid_or_duplicate" : self .scanned
1477+ - self .included
1478+ - self .irrelevant_role ,
1479+ },
13791480 "portfolio_companies_scanned" : len (SEQUOIA_GREENHOUSE_BOARDS ),
13801481 }
13811482
@@ -1468,13 +1569,24 @@ def scrape_job_leads(
14681569 created += 1
14691570 else :
14701571 updated += 1
1572+ rejected_existing = reject_unreviewed_irrelevant_job_leads (
1573+ settings ,
1574+ source_key = adapter .source_key ,
1575+ lead_is_relevant = lambda title , body : _lead_is_coop_role (
1576+ adapter .source_key , title , body
1577+ ),
1578+ current_irrelevant_external_ids = getattr (
1579+ adapter , "irrelevant_external_ids" , set ()
1580+ ),
1581+ )
14711582 return {
14721583 "source" : adapter .source_key ,
14731584 ** collection_report ,
14741585 "created" : created ,
14751586 "updated" : updated ,
14761587 "total" : len (lead_ids ),
14771588 "persisted" : len (lead_ids ),
1589+ "rejected_existing" : rejected_existing ,
14781590 "lead_ids" : lead_ids ,
14791591 }
14801592
0 commit comments