feat: show the German/French drop count in the digest

Jobs rejected for German/French content never reach jobs.csv, so daily.ts
had no way to see they existed — the digest silently under-reported how
much the run actually looked at.

index.ts now counts them across both the LinkedIn and extra-source paths
and writes results/.lang-dropped.json; daily.ts reads it back, keyed on
the digest date, and appends a count-only category. Count-only because
there is no Row to list — these were dropped before ever being parsed.
This commit is contained in:
2026-09-05 18:04:29 +02:00
parent 2b7bf3b5f7
commit af5df25fa1
2 changed files with 23 additions and 1 deletions
+14 -1
View File
@@ -8,6 +8,7 @@ import { matchesProfile } from "./profile.ts";
const CSV = "./results/jobs.csv"; const CSV = "./results/jobs.csv";
const SENT = "./results/sent.txt"; // links already pushed — never resend const SENT = "./results/sent.txt"; // links already pushed — never resend
const LANG_DROPPED = "./results/.lang-dropped.json"; // written by index.ts — jobs dropped for German/French content never reach the CSV
const dry = process.argv.includes("--dry-run"); const dry = process.argv.includes("--dry-run");
const resend = process.argv.includes("--resend"); const resend = process.argv.includes("--resend");
@@ -47,10 +48,19 @@ const sent = new Set(
: [] : []
); );
const dateArg = process.argv.find((a) => a.startsWith("--date="))?.slice(7); const dateArg = process.argv.find((a) => a.startsWith("--date="))?.slice(7);
const digestDate = dateArg ?? new Date().toISOString().slice(0, 10);
const fresh = resend const fresh = resend
? all.filter((r) => r.fetched === (dateArg ?? new Date().toISOString().slice(0, 10))) ? all.filter((r) => r.fetched === digestDate)
: all.filter((r) => !before.has(r.link)); : all.filter((r) => !before.has(r.link));
// Jobs dropped for German/French content never make it into the CSV, so this
// is the only place their count survives — shown as a count-only category,
// never listed, since there's no Row to show for them.
const langDropped = await Bun.file(LANG_DROPPED)
.json()
.then((j) => (j.date === digestDate ? j.count : 0))
.catch(() => 0);
// Only leads that actually match the CV (profile.ts) and haven't been sent before. // Only leads that actually match the CV (profile.ts) and haven't been sent before.
const matched = fresh.filter((r) => !sent.has(r.link) && matchesProfile(r.title, r.company).ok); const matched = fresh.filter((r) => !sent.has(r.link) && matchesProfile(r.title, r.company).ok);
console.log(`${fresh.length} new · ${matched.length} match profile · ${fresh.length - matched.length} filtered or already sent`); console.log(`${fresh.length} new · ${matched.length} match profile · ${fresh.length - matched.length} filtered or already sent`);
@@ -95,6 +105,9 @@ for (const k of order) {
lines.push(`• ${escapeHtml(r.title)} — ${escapeHtml(r.company)} ${label}`); lines.push(`• ${escapeHtml(r.title)} — ${escapeHtml(r.company)} ${label}`);
} }
} }
if (langDropped > 0) {
lines.push(`\n<b>🔴 Ignored (German/French)</b> (${langDropped})`);
}
const msg = lines.join("\n"); const msg = lines.join("\n");
if (dry) { if (dry) {
+9
View File
@@ -54,6 +54,7 @@ try {
const date = new Date().toISOString().slice(0, 10); const date = new Date().toISOString().slice(0, 10);
const newRows: string[] = []; const newRows: string[] = [];
let totalLangDropped = 0; // jobs dropped for German/French content — never reach the CSV, so daily.ts can't see them on its own
function csvCell(val: string): string { function csvCell(val: string): string {
return `"${val.replace(/"/g, '""')}"`; return `"${val.replace(/"/g, '""')}"`;
@@ -111,6 +112,7 @@ async function processLinkedInSearch(config: (typeof searches)[number]): Promise
if (!viable.ok) { if (!viable.ok) {
console.log(`dropped (${viable.reason})`); console.log(`dropped (${viable.reason})`);
langDropped++; langDropped++;
totalLangDropped++;
continue; continue;
} }
console.log("ok"); console.log("ok");
@@ -162,6 +164,7 @@ async function processExtraSource({ label, fetch: fetchFn }: (typeof extraSource
if (!viable.ok) { if (!viable.ok) {
console.log(`dropped (${viable.reason})`); console.log(`dropped (${viable.reason})`);
langDropped++; langDropped++;
totalLangDropped++;
continue; continue;
} }
console.log("ok"); console.log("ok");
@@ -184,6 +187,12 @@ await Promise.all([
runWithConcurrency(extraSources, EXTRA_SOURCE_CONCURRENCY, processExtraSource), runWithConcurrency(extraSources, EXTRA_SOURCE_CONCURRENCY, processExtraSource),
]); ]);
// Jobs dropped for German/French content never reach the CSV, so this is the
// only record of them — daily.ts reads it back to show a count in the digest
// without listing rejects that were never even parsed into a Row.
const LANG_DROPPED = "./results/.lang-dropped.json";
await Bun.write(LANG_DROPPED, JSON.stringify({ date, count: totalLangDropped }));
if (newRows.length === 0) { if (newRows.length === 0) {
console.log("\nNo new jobs."); console.log("\nNo new jobs.");
process.exit(0); process.exit(0);