surface total job board outages
This commit is contained in:
@@ -184,6 +184,17 @@ class MatchmakingAgentSession:
|
|||||||
search.run_sweep(prefs, fresh=fresh),
|
search.run_sweep(prefs, fresh=fresh),
|
||||||
["Scanning live roles across the boards…", "Pulling the latest Naukri + LinkedIn + Foundit roles…",
|
["Scanning live roles across the boards…", "Pulling the latest Naukri + LinkedIn + Foundit roles…",
|
||||||
"Gathering fresh postings for you…"])
|
"Gathering fresh postings for you…"])
|
||||||
|
if sweep.get("all_boards_failed"):
|
||||||
|
await self.push(
|
||||||
|
"agent_error",
|
||||||
|
action="search_failed",
|
||||||
|
data={
|
||||||
|
"code": "job_boards_unavailable",
|
||||||
|
"message": "All enabled job boards are temporarily unavailable.",
|
||||||
|
"failed_boards": sweep.get("failed_boards", 0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
new_count = await _repo.pool_save(self.user_id, sig, sweep["opportunities"])
|
new_count = await _repo.pool_save(self.user_id, sig, sweep["opportunities"])
|
||||||
# tapped out? a sweep that added almost nothing new → flag it so we stop block-fetching dupes.
|
# tapped out? a sweep that added almost nothing new → flag it so we stop block-fetching dupes.
|
||||||
if new_count < s.POOL_MIN_NEW_PER_REFILL:
|
if new_count < s.POOL_MIN_NEW_PER_REFILL:
|
||||||
|
|||||||
@@ -132,8 +132,10 @@ async def run_sweep(prefs: dict, *, fresh: bool = False) -> dict:
|
|||||||
merged: list[dict] = []
|
merged: list[dict] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
sources: dict[str, int] = {}
|
sources: dict[str, int] = {}
|
||||||
|
failed_boards = 0
|
||||||
for r in results:
|
for r in results:
|
||||||
if isinstance(r, Exception):
|
if isinstance(r, Exception):
|
||||||
|
failed_boards += 1
|
||||||
logger.warning("board fetch failed: %s", r)
|
logger.warning("board fetch failed: %s", r)
|
||||||
continue
|
continue
|
||||||
key, jobs = r
|
key, jobs = r
|
||||||
@@ -146,7 +148,13 @@ async def run_sweep(prefs: dict, *, fresh: bool = False) -> dict:
|
|||||||
merged.append(j)
|
merged.append(j)
|
||||||
|
|
||||||
# No ranking here — the engine (app/engine/rank.py) scores + selects after the sweep.
|
# No ranking here — the engine (app/engine/rank.py) scores + selects after the sweep.
|
||||||
return {"opportunities": merged, "sources": sources}
|
return {
|
||||||
|
"opportunities": merged,
|
||||||
|
"sources": sources,
|
||||||
|
"all_boards_failed": bool(enabled) and failed_boards == len(enabled),
|
||||||
|
"failed_boards": failed_boards,
|
||||||
|
"enabled_boards": len(enabled),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def has_mvq(prefs: dict) -> bool:
|
def has_mvq(prefs: dict) -> bool:
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
"""Warm-pool gate — the reorder/floor decision that keeps the shelf stocked without over-fetching."""
|
"""Warm-pool gate — the reorder/floor decision that keeps the shelf stocked without over-fetching."""
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.engine import search
|
||||||
from app.engine.search import pool_decision
|
from app.engine.search import pool_decision
|
||||||
from app.db.repo import search_signature
|
from app.db.repo import search_signature
|
||||||
|
|
||||||
@@ -66,3 +71,21 @@ def test_legacy_and_camel_case_work_mode_have_same_signature():
|
|||||||
common = {"title": "Data Analyst", "location": ["Bangalore · India"]}
|
common = {"title": "Data Analyst", "location": ["Bangalore · India"]}
|
||||||
|
|
||||||
assert search_signature({**common, "workMode": ["Hybrid"]}) == search_signature({**common, "work_mode": ["Hybrid"]})
|
assert search_signature({**common, "workMode": ["Hybrid"]}) == search_signature({**common, "work_mode": ["Hybrid"]})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_sweep_marks_total_board_failure(monkeypatch):
|
||||||
|
async def fail_board(*_args, **_kwargs):
|
||||||
|
raise RuntimeError("provider blocked")
|
||||||
|
|
||||||
|
monkeypatch.setattr(search, "_fetch_board", fail_board)
|
||||||
|
monkeypatch.setattr(search, "get_settings", lambda: SimpleNamespace(
|
||||||
|
BOARDS_ENABLED="naukri,foundit",
|
||||||
|
BOARD_TIMEOUT_S=1,
|
||||||
|
))
|
||||||
|
|
||||||
|
result = await search.run_sweep({"title": "Data Analyst", "location": ["Bangalore · India"]})
|
||||||
|
|
||||||
|
assert result["all_boards_failed"] is True
|
||||||
|
assert result["failed_boards"] == 2
|
||||||
|
assert result["sources"] == {}
|
||||||
|
|||||||
Reference in New Issue
Block a user