feat/Update_Controllers_to_Allow_for_Premium_Membership
This commit is contained in:
@@ -1,92 +1,76 @@
|
||||
using System.Text.Json;
|
||||
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 : BackgroundService
|
||||
public sealed class JobEnrichmentHostedService(
|
||||
BackgroundTenantRunner tenants,
|
||||
IConfiguration configuration,
|
||||
ILogger<JobEnrichmentHostedService> logger,
|
||||
IStartupReadiness startupReadiness) : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<JobEnrichmentHostedService> _logger;
|
||||
private readonly IStartupReadiness _startupReadiness;
|
||||
|
||||
public JobEnrichmentHostedService(IServiceProvider services, ILogger<JobEnrichmentHostedService> logger, IStartupReadiness startupReadiness)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
_startupReadiness = startupReadiness;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _startupReadiness.WaitUntilReadyAsync(stoppingToken);
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<JobTrackerContext>();
|
||||
var summarizer = scope.ServiceProvider.GetRequiredService<ISummarizerService>();
|
||||
|
||||
var jobs = await db.JobApplications
|
||||
.Where(j => !j.IsDeleted)
|
||||
.Where(j => string.IsNullOrWhiteSpace(j.ShortSummary) || string.IsNullOrWhiteSpace(j.Tags))
|
||||
.OrderByDescending(j => j.DateApplied)
|
||||
.Take(20)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
var changed = 0;
|
||||
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(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (tags.Count > 0)
|
||||
{
|
||||
job.Tags = JsonSerializer.Serialize(tags);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(job.ShortSummary) && !string.IsNullOrWhiteSpace(sourceText))
|
||||
{
|
||||
try
|
||||
{
|
||||
var shortSummary = await summarizer.SummarizeAsync(sourceText, 160, 60);
|
||||
if (!string.IsNullOrWhiteSpace(shortSummary))
|
||||
{
|
||||
job.ShortSummary = shortSummary;
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Best effort; leave for a later pass.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed > 0)
|
||||
{
|
||||
await db.SaveChangesAsync(stoppingToken);
|
||||
_logger.LogInformation("Backfilled tags/summaries for {Count} job fields.", changed);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Job enrichment background pass failed.");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user