Files
procyon/autopilot.py
2026-10-06 23:43:21 -07:00

81 lines
2.6 KiB
Python

# Procyon — autopilot. Scheduled discover -> enrich -> score -> auto-prepare top N.
# Everything up to the human approval/send gate. Never sends without approval.
import os
import db
import scraper
import tailor
import pipeline
import emailer
BASE = os.path.dirname(os.path.abspath(__file__))
def run():
db.init_db()
if db.get_setting('autopilot_enabled', '1') != '1':
print('autopilot disabled')
return
# 1. discover
try:
added = scraper.discover()
except Exception as e:
added = 0
print(f'discover error: {e}')
print(f'discovered {added} new job(s)')
# 2. enrich thin descriptions (firecrawl scrape / http fetch)
new_jobs = db.list_jobs(status='new', limit=200)
enriched = 0
for j in new_jobs:
desc = j.get('description') or ''
if len(desc) < 200 and j.get('url'):
try:
full = scraper.enrich_description(j['url'])
if full and len(full) > len(desc):
conn = db.get_conn()
try:
conn.execute('UPDATE jobs SET description = ? WHERE id = ?',
(full[:5000], j['id']))
conn.commit()
finally:
conn.close()
enriched += 1
except Exception:
pass
print(f'enriched {enriched} job description(s)')
# 3. score all 'new' jobs
new_jobs = db.list_jobs(status='new', limit=200)
scored = []
for j in new_jobs:
s, o = tailor.score_job(j)
db.update_job_score(j['id'], s, o)
scored.append((j['id'], s))
print(f'scored {len(scored)} job(s)')
# 4. auto-prepare the top N (tailor + audit + PDF) — queued for your approval
max_prepare = int(db.get_setting('autopilot_max_prepare', '3'))
top = sorted(scored, key=lambda x: -x[1])[:max_prepare]
prepared = 0
for jid, s in top:
job = db.get_job(jid)
if not job:
continue
try:
aid = pipeline.prepare(job)
print(f'prepared app #{aid} for "{job["title"]}" (fit {s})')
prepared += 1
if db.get_setting('auto_send', '0') == '1':
sent, msg = emailer.auto_send(aid)
print(f' auto-send: {msg}' if sent else f' auto-send skipped: {msg}')
except Exception as e:
print(f'prepare failed for {job["title"]}: {e}')
print(f'AUTOPILOT_DONE: {added} discovered, {len(scored)} scored, {prepared} prepared')
if __name__ == '__main__':
run()