# Procyon — Job Hunt Workflow Engine # Multi-step pipeline: discover -> score -> tailor -> audit -> approve -> send -> track import sqlite3 import os from datetime import datetime, timezone DB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') DB = os.path.join(DB_DIR, 'procyon.db') def now(): return datetime.now(timezone.utc).isoformat() def get_conn(): os.makedirs(DB_DIR, exist_ok=True) conn = sqlite3.connect(DB, timeout=15) conn.row_factory = sqlite3.Row return conn SCHEMA = """ CREATE TABLE IF NOT EXISTS jobs ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, company TEXT, location TEXT, url TEXT UNIQUE, source TEXT, description TEXT, posted_at TEXT, discovered_at TEXT, fit_score REAL, opinion TEXT, contact_email TEXT, status TEXT DEFAULT 'new' ); CREATE TABLE IF NOT EXISTS applications ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id INTEGER NOT NULL, resume_path TEXT, tailored_pdf TEXT, email_subject TEXT, email_body TEXT, audit_json TEXT, status TEXT DEFAULT 'draft', outcome TEXT, sent_at TEXT, created_at TEXT, notes TEXT ); CREATE TABLE IF NOT EXISTS outcomes ( id INTEGER PRIMARY KEY AUTOINCREMENT, application_id INTEGER NOT NULL, stage TEXT, happened_at TEXT, notes TEXT ); CREATE TABLE IF NOT EXISTS audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, application_id INTEGER, job_id INTEGER, item TEXT, result TEXT, detail TEXT, checked_at TEXT ); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status); CREATE INDEX IF NOT EXISTS idx_apps_job ON applications(job_id); """ DEFAULTS = { 'llm_base_url': 'http://10.30.20.69:11434', 'llm_model': 'qwen3.5:9b', 'llm_timeout': '120', 'smtp_host': 'smtp.mail.me.com', 'smtp_port': '587', 'smtp_user': '', 'smtp_pass': '', 'from_email': 'indianaholmes1@icloud.com', 'from_name': 'Indiana Holmes', 'resume_phone': '(425) 280-0023', 'resume_email': 'indianaholmes1@icloud.com', 'website': 'https://thetempleofdoom.com', 'proxy_url': '', 'master_resume': '', 'rss_feeds': '', 'headless_browser': '0', 'firecrawl_url': 'http://10.30.20.182:3002', 'firecrawl_enabled': '1', 'firecrawl_queries': 'remote software engineer\nremote devops engineer\nremote platform engineer\nremote linux administrator\nwork from home software engineer seattle\nremote infrastructure engineer', 'autopilot_enabled': '1', 'autopilot_interval_hours': '6', 'autopilot_max_prepare': '3', 'auto_send': '0', } def init_db(): conn = get_conn() try: conn.executescript(SCHEMA) for k, v in DEFAULTS.items(): conn.execute('INSERT OR IGNORE INTO settings(key, value) VALUES (?, ?)', (k, v)) conn.commit() finally: conn.close() def get_settings(): conn = get_conn() try: rows = conn.execute('SELECT key, value FROM settings').fetchall() return {r['key']: r['value'] for r in rows} finally: conn.close() def get_setting(key, default=None): conn = get_conn() try: r = conn.execute('SELECT value FROM settings WHERE key = ?', (key,)).fetchone() return r['value'] if r else default finally: conn.close() def set_setting(key, value): conn = get_conn() try: conn.execute( 'INSERT INTO settings(key, value) VALUES (?, ?) ' 'ON CONFLICT(key) DO UPDATE SET value = excluded.value', (key, str(value)), ) conn.commit() finally: conn.close() # ---- jobs ---- def upsert_job(title, company, location, url, source, description, posted_at=None): conn = get_conn() try: if url: existing = conn.execute('SELECT id FROM jobs WHERE url = ?', (url,)).fetchone() if existing: return existing['id'] cur = conn.execute( 'INSERT INTO jobs(title, company, location, url, source, description, posted_at, discovered_at) ' 'VALUES (?, ?, ?, ?, ?, ?, ?, ?)', (title, company, location, url, source, description, posted_at, now()), ) conn.commit() return cur.lastrowid finally: conn.close() def list_jobs(status=None, limit=100, offset=0): conn = get_conn() try: q = 'SELECT * FROM jobs' args = [] if status: q += ' WHERE status = ?' args.append(status) q += ' ORDER BY COALESCE(fit_score, -1) DESC, discovered_at DESC LIMIT ? OFFSET ?' args += [limit, offset] return [dict(r) for r in conn.execute(q, args).fetchall()] finally: conn.close() def get_job(job_id): conn = get_conn() try: r = conn.execute('SELECT * FROM jobs WHERE id = ?', (job_id,)).fetchone() return dict(r) if r else None finally: conn.close() def set_job_status(job_id, status): conn = get_conn() try: conn.execute('UPDATE jobs SET status = ? WHERE id = ?', (status, job_id)) conn.commit() finally: conn.close() def update_job_score(job_id, score, opinion): conn = get_conn() try: conn.execute('UPDATE jobs SET fit_score = ?, opinion = ?, status = ? WHERE id = ?', (score, opinion, 'scored', job_id)) conn.commit() finally: conn.close() def set_job_contact_email(job_id, email): conn = get_conn() try: conn.execute('UPDATE jobs SET contact_email = ? WHERE id = ?', (email or '', job_id)) conn.commit() finally: conn.close() # ---- applications ---- def create_application(job_id, subject='', body=''): conn = get_conn() try: cur = conn.execute( 'INSERT INTO applications(job_id, email_subject, email_body, created_at) VALUES (?, ?, ?, ?)', (job_id, subject, body, now()), ) conn.commit() return cur.lastrowid finally: conn.close() def update_application(app_id, **fields): conn = get_conn() try: cols = ', '.join(f'{k} = ?' for k in fields) conn.execute(f'UPDATE applications SET {cols} WHERE id = ?', (*fields.values(), app_id)) conn.commit() finally: conn.close() def get_application(app_id): conn = get_conn() try: r = conn.execute('SELECT * FROM applications WHERE id = ?', (app_id,)).fetchone() return dict(r) if r else None finally: conn.close() def list_applications(limit=100): conn = get_conn() try: q = ('SELECT a.*, j.title, j.company, j.url FROM applications a ' 'LEFT JOIN jobs j ON j.id = a.job_id ORDER BY a.id DESC LIMIT ?') return [dict(r) for r in conn.execute(q, (limit,)).fetchall()] finally: conn.close() def add_outcome(app_id, stage, notes=''): conn = get_conn() try: conn.execute('INSERT INTO outcomes(application_id, stage, happened_at, notes) VALUES (?, ?, ?, ?)', (app_id, stage, now(), notes)) conn.execute('UPDATE applications SET outcome = ? WHERE id = ?', (stage, app_id)) conn.commit() finally: conn.close() def add_audit(app_id, job_id, item, result, detail): conn = get_conn() try: conn.execute('INSERT INTO audit_log(application_id, job_id, item, result, detail, checked_at) ' 'VALUES (?, ?, ?, ?, ?, ?)', (app_id, job_id, item, result, detail, now())) conn.commit() finally: conn.close() def get_audit(app_id): conn = get_conn() try: return [dict(r) for r in conn.execute( 'SELECT * FROM audit_log WHERE application_id = ? ORDER BY id', (app_id,)).fetchall()] finally: conn.close()