diff --git a/app/pages/1_Job_Review.py b/app/pages/1_Job_Review.py index 8f2c397..b86b33b 100644 --- a/app/pages/1_Job_Review.py +++ b/app/pages/1_Job_Review.py @@ -12,12 +12,15 @@ from scripts.db import ( DEFAULT_DB, init_db, get_jobs_by_status, update_job_status, update_cover_letter, mark_applied, get_email_leads, ) +from app.cloud_session import resolve_session, get_db_path + +resolve_session("peregrine") st.title("πŸ“‹ Job Review") -init_db(DEFAULT_DB) +init_db(get_db_path()) -_email_leads = get_email_leads(DEFAULT_DB) +_email_leads = get_email_leads(get_db_path()) # ── Sidebar filters ──────────────────────────────────────────────────────────── with st.sidebar: @@ -37,7 +40,7 @@ with st.sidebar: index=0, ) -jobs = get_jobs_by_status(DEFAULT_DB, show_status) +jobs = get_jobs_by_status(get_db_path(), show_status) if remote_only: jobs = [j for j in jobs if j.get("is_remote")] @@ -86,11 +89,11 @@ if show_status == "pending" and _email_leads: with right_l: if st.button("βœ… Approve", key=f"el_approve_{lead_id}", type="primary", use_container_width=True): - update_job_status(DEFAULT_DB, [lead_id], "approved") + update_job_status(get_db_path(), [lead_id], "approved") st.rerun() if st.button("❌ Reject", key=f"el_reject_{lead_id}", use_container_width=True): - update_job_status(DEFAULT_DB, [lead_id], "rejected") + update_job_status(get_db_path(), [lead_id], "rejected") st.rerun() st.divider() @@ -162,7 +165,7 @@ for job in jobs: ) save_col, _ = st.columns([2, 5]) if save_col.button("πŸ’Ύ Save draft", key=f"save_cl_{job_id}"): - update_cover_letter(DEFAULT_DB, job_id, st.session_state[_cl_key]) + update_cover_letter(get_db_path(), job_id, st.session_state[_cl_key]) st.success("Saved!") # Applied date + cover letter preview (applied/synced) @@ -182,11 +185,11 @@ for job in jobs: if show_status == "pending": if st.button("βœ… Approve", key=f"approve_{job_id}", type="primary", use_container_width=True): - update_job_status(DEFAULT_DB, [job_id], "approved") + update_job_status(get_db_path(), [job_id], "approved") st.rerun() if st.button("❌ Reject", key=f"reject_{job_id}", use_container_width=True): - update_job_status(DEFAULT_DB, [job_id], "rejected") + update_job_status(get_db_path(), [job_id], "rejected") st.rerun() elif show_status == "approved": @@ -198,6 +201,6 @@ for job in jobs: use_container_width=True): cl_text = st.session_state.get(f"cl_{job_id}", "") if cl_text: - update_cover_letter(DEFAULT_DB, job_id, cl_text) - mark_applied(DEFAULT_DB, [job_id]) + update_cover_letter(get_db_path(), job_id, cl_text) + mark_applied(get_db_path(), [job_id]) st.rerun() diff --git a/app/pages/5_Interviews.py b/app/pages/5_Interviews.py index 99b5162..09b6c33 100644 --- a/app/pages/5_Interviews.py +++ b/app/pages/5_Interviews.py @@ -36,6 +36,9 @@ from scripts.db import ( get_unread_stage_signals, dismiss_stage_signal, ) from scripts.task_runner import submit_task +from app.cloud_session import resolve_session, get_db_path + +resolve_session("peregrine") _CONFIG_DIR = Path(__file__).parent.parent.parent / "config" _CALENDAR_INTEGRATIONS = ("apple_calendar", "google_calendar") @@ -46,23 +49,23 @@ _calendar_connected = any( st.title("🎯 Interviews") -init_db(DEFAULT_DB) +init_db(get_db_path()) # ── Sidebar: Email sync ──────────────────────────────────────────────────────── with st.sidebar: st.markdown("### πŸ“§ Email Sync") - _email_task = get_task_for_job(DEFAULT_DB, "email_sync", 0) + _email_task = get_task_for_job(get_db_path(), "email_sync", 0) _email_running = _email_task and _email_task["status"] in ("queued", "running") if st.button("πŸ”„ Sync Emails", use_container_width=True, type="primary", disabled=bool(_email_running)): - submit_task(DEFAULT_DB, "email_sync", 0) + submit_task(get_db_path(), "email_sync", 0) st.rerun() if _email_running: @st.fragment(run_every=4) def _email_sidebar_status(): - t = get_task_for_job(DEFAULT_DB, "email_sync", 0) + t = get_task_for_job(get_db_path(), "email_sync", 0) if t and t["status"] in ("queued", "running"): st.info("⏳ Syncing…") else: @@ -99,7 +102,7 @@ STAGE_NEXT_LABEL = { } # ── Data ────────────────────────────────────────────────────────────────────── -jobs_by_stage = get_interview_jobs(DEFAULT_DB) +jobs_by_stage = get_interview_jobs(get_db_path()) # ── Helpers ─────────────────────────────────────────────────────────────────── def _days_ago(date_str: str | None) -> str: @@ -120,8 +123,8 @@ def _days_ago(date_str: str | None) -> str: def _research_modal(job: dict) -> None: job_id = job["id"] st.caption(f"**{job.get('company')}** β€” {job.get('title')}") - research = get_research(DEFAULT_DB, job_id=job_id) - task = get_task_for_job(DEFAULT_DB, "company_research", job_id) + research = get_research(get_db_path(), job_id=job_id) + task = get_task_for_job(get_db_path(), "company_research", job_id) running = task and task["status"] in ("queued", "running") if running: @@ -144,7 +147,7 @@ def _research_modal(job: dict) -> None: "inaccuracies. SearXNG is now available β€” re-run to get verified facts." ) if st.button("πŸ”„ Re-run with live data", key=f"modal_rescrape_{job_id}", type="primary"): - submit_task(DEFAULT_DB, "company_research", job_id) + submit_task(get_db_path(), "company_research", job_id) st.rerun() st.divider() else: @@ -160,14 +163,14 @@ def _research_modal(job: dict) -> None: ) st.markdown(research["raw_output"]) if st.button("πŸ”„ Refresh", key=f"modal_regen_{job_id}", disabled=bool(running)): - submit_task(DEFAULT_DB, "company_research", job_id) + submit_task(get_db_path(), "company_research", job_id) st.rerun() else: st.info("No research brief yet.") if task and task["status"] == "failed": st.error(f"Last attempt failed: {task.get('error', '')}") if st.button("πŸ”¬ Generate now", key=f"modal_gen_{job_id}"): - submit_task(DEFAULT_DB, "company_research", job_id) + submit_task(get_db_path(), "company_research", job_id) st.rerun() @@ -175,7 +178,7 @@ def _research_modal(job: dict) -> None: def _email_modal(job: dict) -> None: job_id = job["id"] st.caption(f"**{job.get('company')}** β€” {job.get('title')}") - contacts = get_contacts(DEFAULT_DB, job_id=job_id) + contacts = get_contacts(get_db_path(), job_id=job_id) if not contacts: st.info("No emails logged yet. Use the form below to add one.") @@ -246,7 +249,7 @@ def _email_modal(job: dict) -> None: body_text = st.text_area("Body / notes", height=80, key=f"body_modal_{job_id}") if st.form_submit_button("πŸ“§ Save contact"): add_contact( - DEFAULT_DB, job_id=job_id, + get_db_path(), job_id=job_id, direction=direction, subject=subject, from_addr=from_addr, body=body_text, received_at=recv_at, ) @@ -255,7 +258,7 @@ def _email_modal(job: dict) -> None: def _render_card(job: dict, stage: str, compact: bool = False) -> None: """Render a single job card appropriate for the given stage.""" job_id = job["id"] - contacts = get_contacts(DEFAULT_DB, job_id=job_id) + contacts = get_contacts(get_db_path(), job_id=job_id) last_contact = contacts[-1] if contacts else None with st.container(border=True): @@ -278,7 +281,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: format="YYYY-MM-DD", ) if st.form_submit_button("πŸ“… Save date"): - set_interview_date(DEFAULT_DB, job_id=job_id, date_str=str(new_date)) + set_interview_date(get_db_path(), job_id=job_id, date_str=str(new_date)) st.success("Saved!") st.rerun() @@ -288,7 +291,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: _cal_label = "πŸ”„ Update Calendar" if _has_event else "πŸ“… Add to Calendar" if st.button(_cal_label, key=f"cal_push_{job_id}", use_container_width=True): from scripts.calendar_push import push_interview_event - result = push_interview_event(DEFAULT_DB, job_id=job_id, config_dir=_CONFIG_DIR) + result = push_interview_event(get_db_path(), job_id=job_id, config_dir=_CONFIG_DIR) if result["ok"]: st.success(f"Event {'updated' if _has_event else 'added'} ({result['provider'].replace('_', ' ').title()})") st.rerun() @@ -297,7 +300,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: if not compact: if stage in ("applied", "phone_screen", "interviewing"): - signals = get_unread_stage_signals(DEFAULT_DB, job_id=job_id) + signals = get_unread_stage_signals(get_db_path(), job_id=job_id) if signals: sig = signals[-1] _SIGNAL_TO_STAGE = { @@ -318,23 +321,23 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: if sig["stage_signal"] == "rejected": if b1.button("βœ— Reject", key=f"sig_rej_{sig['id']}", use_container_width=True): - reject_at_stage(DEFAULT_DB, job_id=job_id, rejection_stage=stage) - dismiss_stage_signal(DEFAULT_DB, sig["id"]) + reject_at_stage(get_db_path(), job_id=job_id, rejection_stage=stage) + dismiss_stage_signal(get_db_path(), sig["id"]) st.rerun(scope="app") elif target_stage and b1.button( f"β†’ {target_label}", key=f"sig_adv_{sig['id']}", use_container_width=True, type="primary", ): if target_stage == "phone_screen" and stage == "applied": - advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") - submit_task(DEFAULT_DB, "company_research", job_id) + advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen") + submit_task(get_db_path(), "company_research", job_id) elif target_stage: - advance_to_stage(DEFAULT_DB, job_id=job_id, stage=target_stage) - dismiss_stage_signal(DEFAULT_DB, sig["id"]) + advance_to_stage(get_db_path(), job_id=job_id, stage=target_stage) + dismiss_stage_signal(get_db_path(), sig["id"]) st.rerun(scope="app") if b2.button("Dismiss", key=f"sig_dis_{sig['id']}", use_container_width=True): - dismiss_stage_signal(DEFAULT_DB, sig["id"]) + dismiss_stage_signal(get_db_path(), sig["id"]) st.rerun() # Advance / Reject buttons @@ -346,16 +349,16 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: f"β†’ {next_label}", key=f"adv_{job_id}", use_container_width=True, type="primary", ): - advance_to_stage(DEFAULT_DB, job_id=job_id, stage=next_stage) + advance_to_stage(get_db_path(), job_id=job_id, stage=next_stage) if next_stage == "phone_screen": - submit_task(DEFAULT_DB, "company_research", job_id) + submit_task(get_db_path(), "company_research", job_id) st.rerun(scope="app") # full rerun β€” card must appear in new column if c2.button( "βœ— Reject", key=f"rej_{job_id}", use_container_width=True, ): - reject_at_stage(DEFAULT_DB, job_id=job_id, rejection_stage=stage) + reject_at_stage(get_db_path(), job_id=job_id, rejection_stage=stage) st.rerun() # fragment-scope rerun β€” card disappears without scroll-to-top if job.get("url"): @@ -385,7 +388,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None: @st.fragment def _card_fragment(job_id: int, stage: str) -> None: """Re-fetches the job on each fragment rerun; renders nothing if moved/rejected.""" - job = get_job_by_id(DEFAULT_DB, job_id) + job = get_job_by_id(get_db_path(), job_id) if job is None or job.get("status") != stage: return _render_card(job, stage) @@ -394,11 +397,11 @@ def _card_fragment(job_id: int, stage: str) -> None: @st.fragment def _pre_kanban_row_fragment(job_id: int) -> None: """Pre-kanban compact row for applied and survey-stage jobs.""" - job = get_job_by_id(DEFAULT_DB, job_id) + job = get_job_by_id(get_db_path(), job_id) if job is None or job.get("status") not in ("applied", "survey"): return stage = job["status"] - contacts = get_contacts(DEFAULT_DB, job_id=job_id) + contacts = get_contacts(get_db_path(), job_id=job_id) last_contact = contacts[-1] if contacts else None with st.container(border=True): @@ -414,7 +417,7 @@ def _pre_kanban_row_fragment(job_id: int) -> None: _email_modal(job) # Stage signal hint (email-detected next steps) - signals = get_unread_stage_signals(DEFAULT_DB, job_id=job_id) + signals = get_unread_stage_signals(get_db_path(), job_id=job_id) if signals: sig = signals[-1] _SIGNAL_TO_STAGE = { @@ -437,15 +440,15 @@ def _pre_kanban_row_fragment(job_id: int) -> None: use_container_width=True, type="primary", ): if target_stage == "phone_screen": - advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") - submit_task(DEFAULT_DB, "company_research", job_id) + advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen") + submit_task(get_db_path(), "company_research", job_id) else: - advance_to_stage(DEFAULT_DB, job_id=job_id, stage=target_stage) - dismiss_stage_signal(DEFAULT_DB, sig["id"]) + advance_to_stage(get_db_path(), job_id=job_id, stage=target_stage) + dismiss_stage_signal(get_db_path(), sig["id"]) st.rerun(scope="app") if s2.button("Dismiss", key=f"sig_dis_pre_{sig['id']}", use_container_width=True): - dismiss_stage_signal(DEFAULT_DB, sig["id"]) + dismiss_stage_signal(get_db_path(), sig["id"]) st.rerun() with right: @@ -453,24 +456,24 @@ def _pre_kanban_row_fragment(job_id: int) -> None: "β†’ πŸ“ž Phone Screen", key=f"adv_pre_{job_id}", use_container_width=True, type="primary", ): - advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") - submit_task(DEFAULT_DB, "company_research", job_id) + advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen") + submit_task(get_db_path(), "company_research", job_id) st.rerun(scope="app") col_a, col_b = st.columns(2) if stage == "applied" and col_a.button( "πŸ“‹ Survey", key=f"to_survey_{job_id}", use_container_width=True, ): - advance_to_stage(DEFAULT_DB, job_id=job_id, stage="survey") + advance_to_stage(get_db_path(), job_id=job_id, stage="survey") st.rerun(scope="app") if col_b.button("βœ— Reject", key=f"rej_pre_{job_id}", use_container_width=True): - reject_at_stage(DEFAULT_DB, job_id=job_id, rejection_stage=stage) + reject_at_stage(get_db_path(), job_id=job_id, rejection_stage=stage) st.rerun() @st.fragment def _hired_card_fragment(job_id: int) -> None: """Compact hired job card β€” shown in the Offer/Hired column.""" - job = get_job_by_id(DEFAULT_DB, job_id) + job = get_job_by_id(get_db_path(), job_id) if job is None or job.get("status") != "hired": return with st.container(border=True): diff --git a/app/pages/6_Interview_Prep.py b/app/pages/6_Interview_Prep.py index 812bdd1..94e79c0 100644 --- a/app/pages/6_Interview_Prep.py +++ b/app/pages/6_Interview_Prep.py @@ -25,11 +25,14 @@ from scripts.db import ( get_task_for_job, ) from scripts.task_runner import submit_task +from app.cloud_session import resolve_session, get_db_path -init_db(DEFAULT_DB) +resolve_session("peregrine") + +init_db(get_db_path()) # ── Job selection ───────────────────────────────────────────────────────────── -jobs_by_stage = get_interview_jobs(DEFAULT_DB) +jobs_by_stage = get_interview_jobs(get_db_path()) active_stages = ["phone_screen", "interviewing", "offer"] active_jobs = [ j for stage in active_stages @@ -100,10 +103,10 @@ col_prep, col_context = st.columns([2, 3]) # ════════════════════════════════════════════════ with col_prep: - research = get_research(DEFAULT_DB, job_id=selected_id) + research = get_research(get_db_path(), job_id=selected_id) # Refresh / generate research - _res_task = get_task_for_job(DEFAULT_DB, "company_research", selected_id) + _res_task = get_task_for_job(get_db_path(), "company_research", selected_id) _res_running = _res_task and _res_task["status"] in ("queued", "running") if not research: @@ -112,13 +115,13 @@ with col_prep: if _res_task and _res_task["status"] == "failed": st.error(f"Last attempt failed: {_res_task.get('error', '')}") if st.button("πŸ”¬ Generate research brief", type="primary", use_container_width=True): - submit_task(DEFAULT_DB, "company_research", selected_id) + submit_task(get_db_path(), "company_research", selected_id) st.rerun() if _res_running: @st.fragment(run_every=3) def _res_status_initial(): - t = get_task_for_job(DEFAULT_DB, "company_research", selected_id) + t = get_task_for_job(get_db_path(), "company_research", selected_id) if t and t["status"] in ("queued", "running"): stage = t.get("stage") or "" lbl = "Queued…" if t["status"] == "queued" else (stage or "Generating… this may take 30–60 seconds") @@ -133,13 +136,13 @@ with col_prep: col_ts, col_btn = st.columns([3, 1]) col_ts.caption(f"Research generated: {generated_at}") if col_btn.button("πŸ”„ Refresh", use_container_width=True, disabled=bool(_res_running)): - submit_task(DEFAULT_DB, "company_research", selected_id) + submit_task(get_db_path(), "company_research", selected_id) st.rerun() if _res_running: @st.fragment(run_every=3) def _res_status_refresh(): - t = get_task_for_job(DEFAULT_DB, "company_research", selected_id) + t = get_task_for_job(get_db_path(), "company_research", selected_id) if t and t["status"] in ("queued", "running"): stage = t.get("stage") or "" lbl = "Queued…" if t["status"] == "queued" else (stage or "Refreshing research…") @@ -311,7 +314,7 @@ with col_context: st.markdown(job.get("description") or "_No description saved for this listing._") with tab_emails: - contacts = get_contacts(DEFAULT_DB, job_id=selected_id) + contacts = get_contacts(get_db_path(), job_id=selected_id) if not contacts: st.info("No contacts logged yet. Use the Interviews page to log emails.") else: diff --git a/app/pages/7_Survey.py b/app/pages/7_Survey.py index d5f00ed..ed986ba 100644 --- a/app/pages/7_Survey.py +++ b/app/pages/7_Survey.py @@ -22,10 +22,13 @@ from scripts.db import ( insert_survey_response, get_survey_responses, ) from scripts.llm_router import LLMRouter +from app.cloud_session import resolve_session, get_db_path + +resolve_session("peregrine") st.title("πŸ“‹ Survey Assistant") -init_db(DEFAULT_DB) +init_db(get_db_path()) # ── Vision service health check ──────────────────────────────────────────────── @@ -40,7 +43,7 @@ def _vision_available() -> bool: vision_up = _vision_available() # ── Job selector ─────────────────────────────────────────────────────────────── -jobs_by_stage = get_interview_jobs(DEFAULT_DB) +jobs_by_stage = get_interview_jobs(get_db_path()) survey_jobs = jobs_by_stage.get("survey", []) other_jobs = ( jobs_by_stage.get("applied", []) + @@ -61,7 +64,7 @@ selected_job_id = st.selectbox( format_func=lambda jid: job_labels[jid], index=0, ) -selected_job = get_job_by_id(DEFAULT_DB, selected_job_id) +selected_job = get_job_by_id(get_db_path(), selected_job_id) # ── LLM prompt builders ──────────────────────────────────────────────────────── _SURVEY_SYSTEM = ( @@ -236,7 +239,7 @@ with right_col: image_path = str(img_file) insert_survey_response( - DEFAULT_DB, + get_db_path(), job_id=selected_job_id, survey_name=survey_name, source=source, @@ -256,7 +259,7 @@ with right_col: # ── History ──────────────────────────────────────────────────────────────────── st.divider() st.subheader("πŸ“‚ Response History") -history = get_survey_responses(DEFAULT_DB, job_id=selected_job_id) +history = get_survey_responses(get_db_path(), job_id=selected_job_id) if not history: st.caption("No saved responses for this job yet.")