fix(cloud): replace DEFAULT_DB with get_db_path() across all Streamlit pages

Pages were hardcoding DEFAULT_DB at import time, meaning cloud-mode
per-user DB routing was silently ignored. Pages affected:
1_Job_Review, 5_Interviews, 6_Interview_Prep, 7_Survey.

Adds resolve_session("peregrine") + get_db_path() pattern to each,
matching the pattern already used in 4_Apply.py.

Fixes #24.
This commit is contained in:
pyr0ball 2026-04-01 07:09:35 -07:00
parent 15dc4b2646
commit 9702646738
4 changed files with 76 additions and 64 deletions

View file

@ -12,12 +12,15 @@ from scripts.db import (
DEFAULT_DB, init_db, get_jobs_by_status, update_job_status, DEFAULT_DB, init_db, get_jobs_by_status, update_job_status,
update_cover_letter, mark_applied, get_email_leads, 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") 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 ──────────────────────────────────────────────────────────── # ── Sidebar filters ────────────────────────────────────────────────────────────
with st.sidebar: with st.sidebar:
@ -37,7 +40,7 @@ with st.sidebar:
index=0, index=0,
) )
jobs = get_jobs_by_status(DEFAULT_DB, show_status) jobs = get_jobs_by_status(get_db_path(), show_status)
if remote_only: if remote_only:
jobs = [j for j in jobs if j.get("is_remote")] 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: with right_l:
if st.button("✅ Approve", key=f"el_approve_{lead_id}", if st.button("✅ Approve", key=f"el_approve_{lead_id}",
type="primary", use_container_width=True): 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() st.rerun()
if st.button("❌ Reject", key=f"el_reject_{lead_id}", if st.button("❌ Reject", key=f"el_reject_{lead_id}",
use_container_width=True): use_container_width=True):
update_job_status(DEFAULT_DB, [lead_id], "rejected") update_job_status(get_db_path(), [lead_id], "rejected")
st.rerun() st.rerun()
st.divider() st.divider()
@ -162,7 +165,7 @@ for job in jobs:
) )
save_col, _ = st.columns([2, 5]) save_col, _ = st.columns([2, 5])
if save_col.button("💾 Save draft", key=f"save_cl_{job_id}"): 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!") st.success("Saved!")
# Applied date + cover letter preview (applied/synced) # Applied date + cover letter preview (applied/synced)
@ -182,11 +185,11 @@ for job in jobs:
if show_status == "pending": if show_status == "pending":
if st.button("✅ Approve", key=f"approve_{job_id}", if st.button("✅ Approve", key=f"approve_{job_id}",
type="primary", use_container_width=True): 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() st.rerun()
if st.button("❌ Reject", key=f"reject_{job_id}", if st.button("❌ Reject", key=f"reject_{job_id}",
use_container_width=True): use_container_width=True):
update_job_status(DEFAULT_DB, [job_id], "rejected") update_job_status(get_db_path(), [job_id], "rejected")
st.rerun() st.rerun()
elif show_status == "approved": elif show_status == "approved":
@ -198,6 +201,6 @@ for job in jobs:
use_container_width=True): use_container_width=True):
cl_text = st.session_state.get(f"cl_{job_id}", "") cl_text = st.session_state.get(f"cl_{job_id}", "")
if cl_text: if cl_text:
update_cover_letter(DEFAULT_DB, job_id, cl_text) update_cover_letter(get_db_path(), job_id, cl_text)
mark_applied(DEFAULT_DB, [job_id]) mark_applied(get_db_path(), [job_id])
st.rerun() st.rerun()

View file

@ -36,6 +36,9 @@ from scripts.db import (
get_unread_stage_signals, dismiss_stage_signal, get_unread_stage_signals, dismiss_stage_signal,
) )
from scripts.task_runner import submit_task 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" _CONFIG_DIR = Path(__file__).parent.parent.parent / "config"
_CALENDAR_INTEGRATIONS = ("apple_calendar", "google_calendar") _CALENDAR_INTEGRATIONS = ("apple_calendar", "google_calendar")
@ -46,23 +49,23 @@ _calendar_connected = any(
st.title("🎯 Interviews") st.title("🎯 Interviews")
init_db(DEFAULT_DB) init_db(get_db_path())
# ── Sidebar: Email sync ──────────────────────────────────────────────────────── # ── Sidebar: Email sync ────────────────────────────────────────────────────────
with st.sidebar: with st.sidebar:
st.markdown("### 📧 Email Sync") 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") _email_running = _email_task and _email_task["status"] in ("queued", "running")
if st.button("🔄 Sync Emails", use_container_width=True, type="primary", if st.button("🔄 Sync Emails", use_container_width=True, type="primary",
disabled=bool(_email_running)): disabled=bool(_email_running)):
submit_task(DEFAULT_DB, "email_sync", 0) submit_task(get_db_path(), "email_sync", 0)
st.rerun() st.rerun()
if _email_running: if _email_running:
@st.fragment(run_every=4) @st.fragment(run_every=4)
def _email_sidebar_status(): 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"): if t and t["status"] in ("queued", "running"):
st.info("⏳ Syncing…") st.info("⏳ Syncing…")
else: else:
@ -99,7 +102,7 @@ STAGE_NEXT_LABEL = {
} }
# ── Data ────────────────────────────────────────────────────────────────────── # ── Data ──────────────────────────────────────────────────────────────────────
jobs_by_stage = get_interview_jobs(DEFAULT_DB) jobs_by_stage = get_interview_jobs(get_db_path())
# ── Helpers ─────────────────────────────────────────────────────────────────── # ── Helpers ───────────────────────────────────────────────────────────────────
def _days_ago(date_str: str | None) -> str: 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: def _research_modal(job: dict) -> None:
job_id = job["id"] job_id = job["id"]
st.caption(f"**{job.get('company')}** — {job.get('title')}") st.caption(f"**{job.get('company')}** — {job.get('title')}")
research = get_research(DEFAULT_DB, job_id=job_id) research = get_research(get_db_path(), job_id=job_id)
task = get_task_for_job(DEFAULT_DB, "company_research", job_id) task = get_task_for_job(get_db_path(), "company_research", job_id)
running = task and task["status"] in ("queued", "running") running = task and task["status"] in ("queued", "running")
if running: if running:
@ -144,7 +147,7 @@ def _research_modal(job: dict) -> None:
"inaccuracies. SearXNG is now available — re-run to get verified facts." "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"): 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.rerun()
st.divider() st.divider()
else: else:
@ -160,14 +163,14 @@ def _research_modal(job: dict) -> None:
) )
st.markdown(research["raw_output"]) st.markdown(research["raw_output"])
if st.button("🔄 Refresh", key=f"modal_regen_{job_id}", disabled=bool(running)): 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() st.rerun()
else: else:
st.info("No research brief yet.") st.info("No research brief yet.")
if task and task["status"] == "failed": if task and task["status"] == "failed":
st.error(f"Last attempt failed: {task.get('error', '')}") st.error(f"Last attempt failed: {task.get('error', '')}")
if st.button("🔬 Generate now", key=f"modal_gen_{job_id}"): 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() st.rerun()
@ -175,7 +178,7 @@ def _research_modal(job: dict) -> None:
def _email_modal(job: dict) -> None: def _email_modal(job: dict) -> None:
job_id = job["id"] job_id = job["id"]
st.caption(f"**{job.get('company')}** — {job.get('title')}") 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: if not contacts:
st.info("No emails logged yet. Use the form below to add one.") 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}") body_text = st.text_area("Body / notes", height=80, key=f"body_modal_{job_id}")
if st.form_submit_button("📧 Save contact"): if st.form_submit_button("📧 Save contact"):
add_contact( add_contact(
DEFAULT_DB, job_id=job_id, get_db_path(), job_id=job_id,
direction=direction, subject=subject, direction=direction, subject=subject,
from_addr=from_addr, body=body_text, received_at=recv_at, 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: def _render_card(job: dict, stage: str, compact: bool = False) -> None:
"""Render a single job card appropriate for the given stage.""" """Render a single job card appropriate for the given stage."""
job_id = job["id"] 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 last_contact = contacts[-1] if contacts else None
with st.container(border=True): with st.container(border=True):
@ -278,7 +281,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None:
format="YYYY-MM-DD", format="YYYY-MM-DD",
) )
if st.form_submit_button("📅 Save date"): 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.success("Saved!")
st.rerun() 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" _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): if st.button(_cal_label, key=f"cal_push_{job_id}", use_container_width=True):
from scripts.calendar_push import push_interview_event 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"]: if result["ok"]:
st.success(f"Event {'updated' if _has_event else 'added'} ({result['provider'].replace('_', ' ').title()})") st.success(f"Event {'updated' if _has_event else 'added'} ({result['provider'].replace('_', ' ').title()})")
st.rerun() st.rerun()
@ -297,7 +300,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None:
if not compact: if not compact:
if stage in ("applied", "phone_screen", "interviewing"): 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: if signals:
sig = signals[-1] sig = signals[-1]
_SIGNAL_TO_STAGE = { _SIGNAL_TO_STAGE = {
@ -318,23 +321,23 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None:
if sig["stage_signal"] == "rejected": if sig["stage_signal"] == "rejected":
if b1.button("✗ Reject", key=f"sig_rej_{sig['id']}", if b1.button("✗ Reject", key=f"sig_rej_{sig['id']}",
use_container_width=True): 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)
dismiss_stage_signal(DEFAULT_DB, sig["id"]) dismiss_stage_signal(get_db_path(), sig["id"])
st.rerun(scope="app") st.rerun(scope="app")
elif target_stage and b1.button( elif target_stage and b1.button(
f"{target_label}", key=f"sig_adv_{sig['id']}", f"{target_label}", key=f"sig_adv_{sig['id']}",
use_container_width=True, type="primary", use_container_width=True, type="primary",
): ):
if target_stage == "phone_screen" and stage == "applied": if target_stage == "phone_screen" and stage == "applied":
advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen")
submit_task(DEFAULT_DB, "company_research", job_id) submit_task(get_db_path(), "company_research", job_id)
elif target_stage: elif target_stage:
advance_to_stage(DEFAULT_DB, job_id=job_id, stage=target_stage) advance_to_stage(get_db_path(), job_id=job_id, stage=target_stage)
dismiss_stage_signal(DEFAULT_DB, sig["id"]) dismiss_stage_signal(get_db_path(), sig["id"])
st.rerun(scope="app") st.rerun(scope="app")
if b2.button("Dismiss", key=f"sig_dis_{sig['id']}", if b2.button("Dismiss", key=f"sig_dis_{sig['id']}",
use_container_width=True): use_container_width=True):
dismiss_stage_signal(DEFAULT_DB, sig["id"]) dismiss_stage_signal(get_db_path(), sig["id"])
st.rerun() st.rerun()
# Advance / Reject buttons # 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}", f"{next_label}", key=f"adv_{job_id}",
use_container_width=True, type="primary", 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": 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 st.rerun(scope="app") # full rerun — card must appear in new column
if c2.button( if c2.button(
"✗ Reject", key=f"rej_{job_id}", "✗ Reject", key=f"rej_{job_id}",
use_container_width=True, 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 st.rerun() # fragment-scope rerun — card disappears without scroll-to-top
if job.get("url"): if job.get("url"):
@ -385,7 +388,7 @@ def _render_card(job: dict, stage: str, compact: bool = False) -> None:
@st.fragment @st.fragment
def _card_fragment(job_id: int, stage: str) -> None: def _card_fragment(job_id: int, stage: str) -> None:
"""Re-fetches the job on each fragment rerun; renders nothing if moved/rejected.""" """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: if job is None or job.get("status") != stage:
return return
_render_card(job, stage) _render_card(job, stage)
@ -394,11 +397,11 @@ def _card_fragment(job_id: int, stage: str) -> None:
@st.fragment @st.fragment
def _pre_kanban_row_fragment(job_id: int) -> None: def _pre_kanban_row_fragment(job_id: int) -> None:
"""Pre-kanban compact row for applied and survey-stage jobs.""" """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"): if job is None or job.get("status") not in ("applied", "survey"):
return return
stage = job["status"] 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 last_contact = contacts[-1] if contacts else None
with st.container(border=True): with st.container(border=True):
@ -414,7 +417,7 @@ def _pre_kanban_row_fragment(job_id: int) -> None:
_email_modal(job) _email_modal(job)
# Stage signal hint (email-detected next steps) # 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: if signals:
sig = signals[-1] sig = signals[-1]
_SIGNAL_TO_STAGE = { _SIGNAL_TO_STAGE = {
@ -437,15 +440,15 @@ def _pre_kanban_row_fragment(job_id: int) -> None:
use_container_width=True, type="primary", use_container_width=True, type="primary",
): ):
if target_stage == "phone_screen": if target_stage == "phone_screen":
advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen")
submit_task(DEFAULT_DB, "company_research", job_id) submit_task(get_db_path(), "company_research", job_id)
else: else:
advance_to_stage(DEFAULT_DB, job_id=job_id, stage=target_stage) advance_to_stage(get_db_path(), job_id=job_id, stage=target_stage)
dismiss_stage_signal(DEFAULT_DB, sig["id"]) dismiss_stage_signal(get_db_path(), sig["id"])
st.rerun(scope="app") st.rerun(scope="app")
if s2.button("Dismiss", key=f"sig_dis_pre_{sig['id']}", if s2.button("Dismiss", key=f"sig_dis_pre_{sig['id']}",
use_container_width=True): use_container_width=True):
dismiss_stage_signal(DEFAULT_DB, sig["id"]) dismiss_stage_signal(get_db_path(), sig["id"])
st.rerun() st.rerun()
with right: with right:
@ -453,24 +456,24 @@ def _pre_kanban_row_fragment(job_id: int) -> None:
"→ 📞 Phone Screen", key=f"adv_pre_{job_id}", "→ 📞 Phone Screen", key=f"adv_pre_{job_id}",
use_container_width=True, type="primary", use_container_width=True, type="primary",
): ):
advance_to_stage(DEFAULT_DB, job_id=job_id, stage="phone_screen") advance_to_stage(get_db_path(), job_id=job_id, stage="phone_screen")
submit_task(DEFAULT_DB, "company_research", job_id) submit_task(get_db_path(), "company_research", job_id)
st.rerun(scope="app") st.rerun(scope="app")
col_a, col_b = st.columns(2) col_a, col_b = st.columns(2)
if stage == "applied" and col_a.button( if stage == "applied" and col_a.button(
"📋 Survey", key=f"to_survey_{job_id}", use_container_width=True, "📋 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") st.rerun(scope="app")
if col_b.button("✗ Reject", key=f"rej_pre_{job_id}", use_container_width=True): 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.rerun()
@st.fragment @st.fragment
def _hired_card_fragment(job_id: int) -> None: def _hired_card_fragment(job_id: int) -> None:
"""Compact hired job card — shown in the Offer/Hired column.""" """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": if job is None or job.get("status") != "hired":
return return
with st.container(border=True): with st.container(border=True):

View file

@ -25,11 +25,14 @@ from scripts.db import (
get_task_for_job, get_task_for_job,
) )
from scripts.task_runner import submit_task 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 ───────────────────────────────────────────────────────────── # ── 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_stages = ["phone_screen", "interviewing", "offer"]
active_jobs = [ active_jobs = [
j for stage in active_stages j for stage in active_stages
@ -100,10 +103,10 @@ col_prep, col_context = st.columns([2, 3])
# ════════════════════════════════════════════════ # ════════════════════════════════════════════════
with col_prep: 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 # 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") _res_running = _res_task and _res_task["status"] in ("queued", "running")
if not research: if not research:
@ -112,13 +115,13 @@ with col_prep:
if _res_task and _res_task["status"] == "failed": if _res_task and _res_task["status"] == "failed":
st.error(f"Last attempt failed: {_res_task.get('error', '')}") st.error(f"Last attempt failed: {_res_task.get('error', '')}")
if st.button("🔬 Generate research brief", type="primary", use_container_width=True): 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() st.rerun()
if _res_running: if _res_running:
@st.fragment(run_every=3) @st.fragment(run_every=3)
def _res_status_initial(): 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"): if t and t["status"] in ("queued", "running"):
stage = t.get("stage") or "" stage = t.get("stage") or ""
lbl = "Queued…" if t["status"] == "queued" else (stage or "Generating… this may take 3060 seconds") lbl = "Queued…" if t["status"] == "queued" else (stage or "Generating… this may take 3060 seconds")
@ -133,13 +136,13 @@ with col_prep:
col_ts, col_btn = st.columns([3, 1]) col_ts, col_btn = st.columns([3, 1])
col_ts.caption(f"Research generated: {generated_at}") col_ts.caption(f"Research generated: {generated_at}")
if col_btn.button("🔄 Refresh", use_container_width=True, disabled=bool(_res_running)): 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() st.rerun()
if _res_running: if _res_running:
@st.fragment(run_every=3) @st.fragment(run_every=3)
def _res_status_refresh(): 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"): if t and t["status"] in ("queued", "running"):
stage = t.get("stage") or "" stage = t.get("stage") or ""
lbl = "Queued…" if t["status"] == "queued" else (stage or "Refreshing research…") 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._") st.markdown(job.get("description") or "_No description saved for this listing._")
with tab_emails: 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: if not contacts:
st.info("No contacts logged yet. Use the Interviews page to log emails.") st.info("No contacts logged yet. Use the Interviews page to log emails.")
else: else:

View file

@ -22,10 +22,13 @@ from scripts.db import (
insert_survey_response, get_survey_responses, insert_survey_response, get_survey_responses,
) )
from scripts.llm_router import LLMRouter from scripts.llm_router import LLMRouter
from app.cloud_session import resolve_session, get_db_path
resolve_session("peregrine")
st.title("📋 Survey Assistant") st.title("📋 Survey Assistant")
init_db(DEFAULT_DB) init_db(get_db_path())
# ── Vision service health check ──────────────────────────────────────────────── # ── Vision service health check ────────────────────────────────────────────────
@ -40,7 +43,7 @@ def _vision_available() -> bool:
vision_up = _vision_available() vision_up = _vision_available()
# ── Job selector ─────────────────────────────────────────────────────────────── # ── 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", []) survey_jobs = jobs_by_stage.get("survey", [])
other_jobs = ( other_jobs = (
jobs_by_stage.get("applied", []) + jobs_by_stage.get("applied", []) +
@ -61,7 +64,7 @@ selected_job_id = st.selectbox(
format_func=lambda jid: job_labels[jid], format_func=lambda jid: job_labels[jid],
index=0, 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 ──────────────────────────────────────────────────────── # ── LLM prompt builders ────────────────────────────────────────────────────────
_SURVEY_SYSTEM = ( _SURVEY_SYSTEM = (
@ -236,7 +239,7 @@ with right_col:
image_path = str(img_file) image_path = str(img_file)
insert_survey_response( insert_survey_response(
DEFAULT_DB, get_db_path(),
job_id=selected_job_id, job_id=selected_job_id,
survey_name=survey_name, survey_name=survey_name,
source=source, source=source,
@ -256,7 +259,7 @@ with right_col:
# ── History ──────────────────────────────────────────────────────────────────── # ── History ────────────────────────────────────────────────────────────────────
st.divider() st.divider()
st.subheader("📂 Response History") 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: if not history:
st.caption("No saved responses for this job yet.") st.caption("No saved responses for this job yet.")