mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
fix(html-report): funnel from stage history, rejection rate from true rejections
The funnel was computed from current status - a state, not a history - so an application that interviewed and was later rejected never counted as reaching Interview, and a hired candidate produced Interview=0. The stage checkboxes Step 1.2 already merges from outcome.md are the history; Step 2 and chart 4 now use them. The rejection rate also counted offer_declined (a success) and withdrawn (candidate-initiated) as rejections and left unresolved Interview/Offer rows in the denominator. Review findings F10 and F11 (2026-08-19). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
dab215073e
commit
2c3d2d8558
@@ -49,8 +49,8 @@ From the normalised data compute:
|
||||
- **By sector:** count per unique sector value
|
||||
- **By channel:** portal vs online vs referral vs other
|
||||
- **By year/season:** group by the `date` field (which may be a year like `2025` or a full date)
|
||||
- **Funnel rates:** what % progressed past resume screen (reached Interview or beyond)
|
||||
- **Rejection rate:** Rejected/Closed ÷ Total with a resolved status (exclude Active)
|
||||
- **Funnel rates:** what % progressed past resume screen (reached Interview or beyond). Compute stage-reached from history, not current status: an application counts as having reached a stage when its current status implies it **or** its merged `outcome.md` stage checkboxes (Step 1.2) show the stage was reached - a `rejected` row whose outcome file ticks an interview stage reached Interview, and a `hired` row reached every stage before Hired. Current status alone structurally undercounts every earlier stage: a finished search would read as though nobody ever interviewed.
|
||||
- **Rejection rate:** true rejections (`rejected`, `no_response`) ÷ applications with a final outcome. `offer_declined` (the candidate turned the offer down - a success) and `withdrawn` (candidate-initiated) are not rejections and stay out of the numerator; Interview and Offer rows are still unresolved, so they stay out of the denominator along with Active. The Rejected/Closed status *bucket* still groups all closed rows for the doughnut - the rate just must not reuse the bucket blindly.
|
||||
|
||||
---
|
||||
|
||||
@@ -106,7 +106,7 @@ Write a single self-contained HTML file. All CSS is inline in a `<style>` block.
|
||||
1. **Status doughnut** — slices for each status bucket, colours from the palette above
|
||||
2. **By sector bar** (horizontal) — company count per sector, sorted descending
|
||||
3. **By channel bar** — online / referral / other
|
||||
4. **Application funnel** (horizontal bar) — Applied → Interview → Offer → Hired, each bar = count reaching that stage
|
||||
4. **Application funnel** (horizontal bar) — Applied → Interview → Offer → Hired, each bar = count reaching that stage, derived per Step 2's funnel rule (current status **plus** the merged `outcome.md` stage checkboxes), so a candidate who interviewed and was later rejected still counts in the Interview bar
|
||||
|
||||
Build each chart as a hand-written `<svg>` element: compute bar lengths/doughnut arc angles from the stats in Step 2 and emit the `<rect>`/`<path>`/`<circle>` and `<text>` elements directly — no charting library, no `<canvas>`. Each `<svg>` has `role="img"` and an `aria-label` summarizing the chart (e.g. "Status breakdown: 3 Active, 2 Interview, 1 Offer"). Wrap each in a `<div class="chart-card">` with an `<h3>` title above. Remember to escape any label/value text drawn into `<text>` nodes per the escaping rule above.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user