# CV builder, parser, benchmark, and Ollama admin integration ## What changed This branch upgrades the Profile CV flow from a text-only rewrite helper into a template-driven CV builder backed by the server-side renderer/PDF pipeline, strengthens CV normalization around location and qualification handling, adds a repeatable local corpus benchmark workflow, and expands the admin system page with richer Ollama visibility. ## Profile CV builder ### New backend capabilities `JobTrackerApi/Controllers/ProfileCvController.cs` - Hardened `POST /api/profile-cv/rewrite-section` - accepts flexible `jobApplicationId` payloads (number or blank string) - uses richer saved-job context for tailoring - logs empty AI responses with useful context - Added `GET /api/profile-cv/templates` - Added `POST /api/profile-cv/rewrite-preview` - rewrites either the whole CV or one selected section - rebuilds structured CV from the rewritten full text - maps the result into the shared template renderer - returns rendered HTML, file name, rewritten text, and full replacement text - Added `POST /api/profile-cv/export-pdf` - uses the same rendered HTML and the shared Playwright exporter ### Frontend flow `job-tracker-ui/src/pages/ProfilePage.tsx` - Replaced the old rewrite draft box with a template-driven builder section. - Users can: - choose from 6 templates - optionally target one section - target by free-text role or saved job - inspect the rewritten content - inspect the actual rendered preview - download a PDF - replace the master CV with the rebuilt full-text result ## Templates Shared renderer: `JobTrackerApi/Services/CvTemplateRenderer.cs` Available templates: - `ats-minimal` - `harvard` - `auckland` - `edinburgh` - `monarch` - `fjord` ### Adding a new template 1. Add the new template id to `NormalizeTemplateId()` in: - `JobTrackerApi/Services/CvTemplateRenderer.cs` - `JobTrackerApi/Controllers/ProfileCvController.cs` 2. Add a render branch in `CvTemplateRenderer.Render()`. 3. Add a descriptor to `GetCvTemplateDescriptors()`. 4. Add the matching card entry in `job-tracker-ui/src/pages/ProfilePage.tsx` if you want a custom preview card. ## PDF generation The master CV builder now reuses the existing server-side pipeline: 1. rewrite full text / section 2. rebuild structured CV 3. map to `TailoredCvDocument` 4. render HTML via `ICvTemplateRenderer` 5. export PDF via `ICvPdfExporter` / Playwright This keeps PDF output visually aligned with the selected template and avoids a separate client-only print implementation. ## Parser and structured CV changes ### Shared schema `Models/StructuredCvProfile.cs` Added: - `education[].qualificationLevel` - top-level `certifications[]` - top-level `projects[]` `qualification` remains the original preserved text. ### Normalization improvements `Models/StructuredCvProfileJson.cs` - tighter location sanitization to avoid skill or role spillover into location fields - qualification level normalization to one of: - `Secondary` - `Diploma/Certificate` - `Bachelor` - `Master` - `PhD` - `Other` - first-class normalization for certifications and projects - section reconstruction now includes certifications and projects ### Extraction prompt improvements `JobTrackerApi/Controllers/ProfileCvController.cs` The LLM extraction prompt now explicitly asks for: - qualification level enum - certifications - projects - strict location separation rules - preservation of original qualification text ## Benchmark workflow ### Runner Use: ```bash ./scripts/run-cv-benchmark.sh ``` Optional overrides: ```bash CV_BENCHMARK_OUTPUT_DIR=/absolute/output/path \ CV_BENCHMARK_APPROVED_DIR=/absolute/approved/fixtures/path \ ./scripts/run-cv-benchmark.sh ``` ### Inputs The runner scans: - `/home/pi/cvs` Supported corpus file types: - PDF - DOCX - TXT - MD ### Outputs The runner writes: - `index.json` — machine-readable summary - `report.md` — markdown overview - `outputs/*.json` — latest normalized structured output per CV - `candidate-fixtures/*.json` — created when no approved fixture exists yet Approved fixtures are local by design and should be reviewed manually before being promoted into the approved fixture path you use for regression comparisons. ### Admin review `GET /api/admin/system/cv-benchmark` The admin system page surfaces: - benchmark root path - last benchmark update time - latest markdown summary ## Ollama admin visibility ### Python health endpoint `tools/summarizer/app.py` `GET /health` now returns additional Ollama metadata when configured/reachable: - `ollama_version` - `ollama_installed_models` - `ollama_loaded_models` - `ollama_loaded_count` ### Backend propagation `JobTrackerApi/Services/SummarizerService.cs` The backend metrics shape now carries those fields through to admin consumers. ### Admin UI `job-tracker-ui/src/pages/AdminSystemPage.tsx` The system page now shows: - Ollama version - loaded model count - installed model chips - loaded model chips - benchmark summary panel ## Verification used on this branch ### Backend ```bash dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter ProfileCvControllerTests dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter "ProfileCvControllerTests|AuthAndSystemControllerTests|JobApplicationsApplicationPackageTests" dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter CvCorpusHarnessTests ``` ### Frontend ```bash cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/profile-page.test.tsx cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/admin-system-page.test.tsx cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/profile-page.test.tsx src/admin-system-page.test.tsx src/job-details-generated-drafts.test.tsx ``` ### Benchmark runner ```bash CV_BENCHMARK_OUTPUT_DIR="$(pwd)/tmp/cv-benchmarks/latest" \ CV_BENCHMARK_APPROVED_DIR="$(pwd)/tmp/cv-benchmarks/approved" \ ./scripts/run-cv-benchmark.sh ``` ### Python service tests The summarizer Python unit tests were updated for the new health payload, but this machine currently lacks `pip` / `venv` support (`python3 -m venv` fails because `python3.12-venv` is not installed), so test execution is environment-blocked here. Once Python packaging is available, run: ```bash cd tools/summarizer python3 -m pytest -q tests/test_app.py ```