Files
jobtrackingapp/JobTrackerApi/Services/JobEnrichmentHostedService.cs
T

77 lines
3.3 KiB
C#

using System.Text.Json;
using JobTrackerApi.Data;
using JobTrackerApi.Services.JobImport;
using JobTrackerApi.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace JobTrackerApi.Services;
public sealed class JobEnrichmentHostedService(
BackgroundTenantRunner tenants,
IConfiguration configuration,
ILogger<JobEnrichmentHostedService> logger,
IStartupReadiness startupReadiness) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await startupReadiness.WaitUntilReadyAsync(stoppingToken);
if (!configuration.GetValue("Workers:JobEnrichmentEnabled", false))
{
logger.LogInformation("Job enrichment worker disabled (Workers:JobEnrichmentEnabled=false).");
return;
}
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
await RunOnceAsync(stoppingToken);
await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken);
}
}
public Task<BackgroundWorkerRunResult> RunOnceAsync(CancellationToken cancellationToken)
{
if (!configuration.GetValue("Workers:JobEnrichmentEnabled", false))
return Task.FromResult(BackgroundWorkerRunResult.Disabled);
return tenants.RunForJobOwnersAsync("job-enrichment", ProcessOwnerAsync, cancellationToken);
}
private static async Task ProcessOwnerAsync(IServiceProvider services, CancellationToken cancellationToken)
{
var db = services.GetRequiredService<JobTrackerContext>();
var userId = db.CurrentUserId;
var users = services.GetRequiredService<UserManager<ApplicationUser>>();
var user = string.IsNullOrWhiteSpace(userId) ? null : await users.FindByIdAsync(userId);
var canUseAi = user is not null && user.AiEnabled && AccountPlans.ForRoles(await users.GetRolesAsync(user)).Ai;
var summarizer = services.GetRequiredService<ISummarizerService>();
var jobs = await db.JobApplications
.Where(job => !job.IsDeleted)
.Where(job => string.IsNullOrWhiteSpace(job.ShortSummary) || string.IsNullOrWhiteSpace(job.Tags))
.OrderByDescending(job => job.DateApplied)
.Take(20)
.ToListAsync(cancellationToken);
foreach (var job in jobs)
{
var sourceText = string.IsNullOrWhiteSpace(job.Description) ? job.Notes : job.Description;
if (string.IsNullOrWhiteSpace(job.Tags) && !string.IsNullOrWhiteSpace(sourceText))
{
var tags = SkillTagger.Detect(sourceText)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(tag => tag, StringComparer.OrdinalIgnoreCase)
.ToList();
if (tags.Count > 0) job.Tags = JsonSerializer.Serialize(tags);
}
if (canUseAi && string.IsNullOrWhiteSpace(job.ShortSummary) && !string.IsNullOrWhiteSpace(sourceText))
{
var summary = await summarizer.SummarizeAsync(sourceText, 160, 60);
if (!string.IsNullOrWhiteSpace(summary)) job.ShortSummary = summary;
}
}
await db.SaveChangesAsync(cancellationToken);
}
}