feat: complete phase 3 career workspace
CI and Deploy / test (push) Failing after 1m6s
CI and Deploy / deploy (push) Has been skipped

This commit is contained in:
cesnimda
2026-07-30 22:19:13 +02:00
parent f8a7cf5205
commit 4cf26405f6
18 changed files with 215 additions and 20 deletions
@@ -53,9 +53,18 @@ public sealed class ProfileCvController : ControllerBase
["languages"] = "Languages",
["interests"] = "Interests",
["hobbies"] = "Interests",
["awards"] = "Awards",
["honours"] = "Awards",
["honors"] = "Awards",
["publications"] = "Publications",
["organisations"] = "Organisations",
["organizations"] = "Organisations",
["memberships"] = "Organisations",
["references"] = "References",
};
private const long MaxFileSizeBytes = 5 * 1024 * 1024;
private const int ExtractionRunRetentionCount = 20;
private const string ParserVersion = "m005-s01";
private const string NormalizerVersion = "m005-s01";
private const string LlmPromptVersion = "m005-s01";
@@ -168,6 +177,7 @@ public sealed class ProfileCvController : ControllerBase
run.Status = "pending_review";
run.CompletedAtUtc = DateTimeOffset.UtcNow;
await _db.SaveChangesAsync(HttpContext.RequestAborted);
await PruneExtractionRunsAsync(user.Id, HttpContext.RequestAborted);
return Ok(new
{
@@ -185,6 +195,7 @@ public sealed class ProfileCvController : ControllerBase
run.ErrorMessage = ex.Message;
run.CompletedAtUtc = DateTimeOffset.UtcNow;
await _db.SaveChangesAsync(HttpContext.RequestAborted);
await PruneExtractionRunsAsync(user.Id, HttpContext.RequestAborted);
throw;
}
}
@@ -625,7 +636,7 @@ public sealed class ProfileCvController : ControllerBase
if (string.IsNullOrWhiteSpace(candidateName)) candidateName = user.UserName?.Trim();
if (string.IsNullOrWhiteSpace(candidateName)) candidateName = user.Email?.Trim();
if (string.IsNullOrWhiteSpace(candidateName)) candidateName = "Your Name";
return _cvTemplateRenderer.Render(document, document.TemplateId, candidateName!, targetRole, companyName, user.AvatarImageDataUrl);
return _cvTemplateRenderer.Render(document, document.TemplateId, candidateName!, targetRole, companyName, AvatarStorage.Resolve(user.AvatarImageDataUrl));
}
private static TailoredCvDocument BuildMasterCvDocument(StructuredCvProfile structuredCv, string templateId, string? targetRole, string? fallbackHeadline, string? companyName)
@@ -903,6 +914,7 @@ public sealed class ProfileCvController : ControllerBase
}
await _db.SaveChangesAsync(cancellationToken);
await PruneExtractionRunsAsync(user.Id, cancellationToken);
}
private async Task<CvExtractionRun> CreateQueuedRunAsync(string ownerUserId, int? artifactId, string trigger, CancellationToken cancellationToken)
@@ -1011,6 +1023,7 @@ public sealed class ProfileCvController : ControllerBase
run.ErrorMessage = ex.Message;
run.CompletedAtUtc = DateTimeOffset.UtcNow;
await _db.SaveChangesAsync(cancellationToken);
await PruneExtractionRunsAsync(user.Id, cancellationToken);
await SendRunCompletionEmailAsync(user, run, false, cancellationToken);
_logger.LogWarning(ex, "CV processing run {RunId} failed for user {UserId}", run.Id, user.Id);
}
@@ -1024,6 +1037,19 @@ public sealed class ProfileCvController : ControllerBase
run.Status = "pending_review";
run.CompletedAtUtc = DateTimeOffset.UtcNow;
await _db.SaveChangesAsync(cancellationToken);
await PruneExtractionRunsAsync(run.OwnerUserId, cancellationToken);
}
private async Task PruneExtractionRunsAsync(string ownerUserId, CancellationToken cancellationToken)
{
var expired = await _db.CvExtractionRuns
.Where(x => x.OwnerUserId == ownerUserId && x.Status != "queued" && x.Status != "running")
.OrderByDescending(x => x.StartedAtUtc)
.Skip(ExtractionRunRetentionCount)
.ToListAsync(cancellationToken);
if (expired.Count == 0) return;
_db.CvExtractionRuns.RemoveRange(expired);
await _db.SaveChangesAsync(cancellationToken);
}
private async Task SendRunCompletionEmailAsync(ApplicationUser user, CvExtractionRun run, bool success, CancellationToken cancellationToken)