196 lines
7.6 KiB
C#
196 lines
7.6 KiB
C#
|
|
#!/usr/bin/env dotnet-script
|
||
|
|
#r "nuget: Microsoft.EntityFrameworkCore.SqlServer, 8.0.0"
|
||
|
|
#r "nuget: Microsoft.Extensions.Configuration.Json, 8.0.0"
|
||
|
|
#r "nuget: Microsoft.Extensions.DependencyInjection, 8.0.0"
|
||
|
|
#r "nuget: Microsoft.Extensions.Logging.Console, 8.0.0"
|
||
|
|
#r "../src/RealCV.Application/bin/Debug/net8.0/RealCV.Application.dll"
|
||
|
|
#r "../src/RealCV.Infrastructure/bin/Debug/net8.0/RealCV.Infrastructure.dll"
|
||
|
|
#r "../src/RealCV.Domain/bin/Debug/net8.0/RealCV.Domain.dll"
|
||
|
|
|
||
|
|
// This is a dotnet-script file. Run with: dotnet script batch-test-cvs.cs -- /path/to/cvs
|
||
|
|
// Install dotnet-script: dotnet tool install -g dotnet-script
|
||
|
|
|
||
|
|
using System;
|
||
|
|
using System.IO;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Text.Json;
|
||
|
|
using Microsoft.EntityFrameworkCore;
|
||
|
|
using Microsoft.Extensions.Configuration;
|
||
|
|
using Microsoft.Extensions.DependencyInjection;
|
||
|
|
using Microsoft.Extensions.Logging;
|
||
|
|
using RealCV.Application.Interfaces;
|
||
|
|
using RealCV.Application.Models;
|
||
|
|
using RealCV.Infrastructure.Data;
|
||
|
|
using RealCV.Infrastructure.Services;
|
||
|
|
using RealCV.Infrastructure.ExternalApis;
|
||
|
|
using RealCV.Infrastructure.Configuration;
|
||
|
|
|
||
|
|
var folderPath = Args.FirstOrDefault() ?? "/tmp/test-cvs";
|
||
|
|
|
||
|
|
if (!Directory.Exists(folderPath))
|
||
|
|
{
|
||
|
|
Console.WriteLine($"Error: Folder not found: {folderPath}");
|
||
|
|
Console.WriteLine("Usage: dotnet script batch-test-cvs.cs -- /path/to/cvs");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
Console.WriteLine($"Processing CVs from: {folderPath}");
|
||
|
|
Console.WriteLine(new string('=', 80));
|
||
|
|
|
||
|
|
// Setup DI
|
||
|
|
var services = new ServiceCollection();
|
||
|
|
|
||
|
|
var configuration = new ConfigurationBuilder()
|
||
|
|
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../src/RealCV.Web"))
|
||
|
|
.AddJsonFile("appsettings.json", optional: true)
|
||
|
|
.AddJsonFile("appsettings.Development.json", optional: true)
|
||
|
|
.Build();
|
||
|
|
|
||
|
|
services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Warning));
|
||
|
|
|
||
|
|
services.AddDbContextFactory<ApplicationDbContext>(options =>
|
||
|
|
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
|
||
|
|
|
||
|
|
services.Configure<CompaniesHouseSettings>(configuration.GetSection("CompaniesHouse"));
|
||
|
|
services.Configure<AnthropicSettings>(configuration.GetSection("Anthropic"));
|
||
|
|
|
||
|
|
services.AddHttpClient<CompaniesHouseClient>();
|
||
|
|
services.AddHttpClient<AnthropicClient>();
|
||
|
|
services.AddScoped<ICompanyNameMatcherService, CompanyNameMatcherService>();
|
||
|
|
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
|
||
|
|
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
|
||
|
|
services.AddScoped<ICVParserService, CVParserService>();
|
||
|
|
|
||
|
|
var provider = services.BuildServiceProvider();
|
||
|
|
|
||
|
|
var cvFiles = Directory.GetFiles(folderPath, "*.*")
|
||
|
|
.Where(f => f.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase) ||
|
||
|
|
f.EndsWith(".docx", StringComparison.OrdinalIgnoreCase))
|
||
|
|
.ToList();
|
||
|
|
|
||
|
|
Console.WriteLine($"Found {cvFiles.Count} CV files\n");
|
||
|
|
|
||
|
|
var allUnverifiedEmployers = new List<string>();
|
||
|
|
var allUnverifiedInstitutions = new List<string>();
|
||
|
|
var totalEmployers = 0;
|
||
|
|
var verifiedEmployers = 0;
|
||
|
|
var totalEducation = 0;
|
||
|
|
var verifiedEducation = 0;
|
||
|
|
|
||
|
|
foreach (var cvFile in cvFiles)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"\n{'=',-80}");
|
||
|
|
Console.WriteLine($"FILE: {Path.GetFileName(cvFile)}");
|
||
|
|
Console.WriteLine($"{'=',-80}");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using var scope = provider.CreateScope();
|
||
|
|
var parser = scope.ServiceProvider.GetRequiredService<ICVParserService>();
|
||
|
|
var companyVerifier = scope.ServiceProvider.GetRequiredService<ICompanyVerifierService>();
|
||
|
|
var eduVerifier = scope.ServiceProvider.GetRequiredService<IEducationVerifierService>();
|
||
|
|
|
||
|
|
await using var stream = File.OpenRead(cvFile);
|
||
|
|
var cv = await parser.ParseAsync(stream, Path.GetFileName(cvFile));
|
||
|
|
|
||
|
|
Console.WriteLine($"Candidate: {cv.PersonalInfo?.FullName ?? "Unknown"}");
|
||
|
|
|
||
|
|
// Employers
|
||
|
|
if (cv.Employment?.Count > 0)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"\nEMPLOYERS ({cv.Employment.Count}):");
|
||
|
|
foreach (var emp in cv.Employment)
|
||
|
|
{
|
||
|
|
totalEmployers++;
|
||
|
|
var result = await companyVerifier.VerifyCompanyAsync(
|
||
|
|
emp.CompanyName, emp.StartDate, emp.EndDate, emp.JobTitle);
|
||
|
|
|
||
|
|
var icon = result.IsVerified ? "✓" : "✗";
|
||
|
|
Console.WriteLine($" {icon} {emp.CompanyName}");
|
||
|
|
|
||
|
|
if (result.IsVerified)
|
||
|
|
{
|
||
|
|
verifiedEmployers++;
|
||
|
|
Console.WriteLine($" → {result.MatchedCompanyName} ({result.MatchScore}%)");
|
||
|
|
if (!string.IsNullOrEmpty(result.VerificationNotes))
|
||
|
|
Console.WriteLine($" Note: {result.VerificationNotes}");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
allUnverifiedEmployers.Add(emp.CompanyName);
|
||
|
|
Console.WriteLine($" Note: {result.VerificationNotes ?? "Not found"}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Education
|
||
|
|
if (cv.Education?.Count > 0)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"\nEDUCATION ({cv.Education.Count}):");
|
||
|
|
var eduEntries = cv.Education.Select(e => new EducationEntry
|
||
|
|
{
|
||
|
|
Institution = e.Institution,
|
||
|
|
Qualification = e.Qualification,
|
||
|
|
Subject = e.Subject,
|
||
|
|
StartDate = e.StartDate,
|
||
|
|
EndDate = e.EndDate
|
||
|
|
}).ToList();
|
||
|
|
|
||
|
|
var eduResults = eduVerifier.VerifyAll(eduEntries);
|
||
|
|
foreach (var result in eduResults)
|
||
|
|
{
|
||
|
|
totalEducation++;
|
||
|
|
var icon = result.IsVerified ? "✓" : "✗";
|
||
|
|
Console.WriteLine($" {icon} {result.ClaimedInstitution}");
|
||
|
|
Console.WriteLine($" {result.ClaimedQualification}");
|
||
|
|
|
||
|
|
if (result.IsVerified)
|
||
|
|
{
|
||
|
|
verifiedEducation++;
|
||
|
|
if (result.MatchedInstitution != null && result.MatchedInstitution != result.ClaimedInstitution)
|
||
|
|
Console.WriteLine($" → {result.MatchedInstitution}");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
allUnverifiedInstitutions.Add(result.ClaimedInstitution ?? "Unknown");
|
||
|
|
Console.WriteLine($" Status: {result.Status}");
|
||
|
|
if (!string.IsNullOrEmpty(result.VerificationNotes))
|
||
|
|
Console.WriteLine($" Note: {result.VerificationNotes}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"ERROR: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Summary
|
||
|
|
Console.WriteLine($"\n\n{'=',-80}");
|
||
|
|
Console.WriteLine("SUMMARY");
|
||
|
|
Console.WriteLine($"{'=',-80}");
|
||
|
|
Console.WriteLine($"CVs Processed: {cvFiles.Count}");
|
||
|
|
Console.WriteLine($"Employers: {verifiedEmployers}/{totalEmployers} verified ({(totalEmployers > 0 ? verifiedEmployers * 100 / totalEmployers : 0)}%)");
|
||
|
|
Console.WriteLine($"Education: {verifiedEducation}/{totalEducation} verified ({(totalEducation > 0 ? verifiedEducation * 100 / totalEducation : 0)}%)");
|
||
|
|
|
||
|
|
var uniqueUnverifiedEmployers = allUnverifiedEmployers.Distinct().OrderBy(x => x).ToList();
|
||
|
|
if (uniqueUnverifiedEmployers.Count > 0)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"\nUNVERIFIED EMPLOYERS ({uniqueUnverifiedEmployers.Count}):");
|
||
|
|
foreach (var emp in uniqueUnverifiedEmployers)
|
||
|
|
Console.WriteLine($" - {emp}");
|
||
|
|
}
|
||
|
|
|
||
|
|
var uniqueUnverifiedInstitutions = allUnverifiedInstitutions.Distinct().OrderBy(x => x).ToList();
|
||
|
|
if (uniqueUnverifiedInstitutions.Count > 0)
|
||
|
|
{
|
||
|
|
Console.WriteLine($"\nUNVERIFIED INSTITUTIONS ({uniqueUnverifiedInstitutions.Count}):");
|
||
|
|
foreach (var inst in uniqueUnverifiedInstitutions)
|
||
|
|
Console.WriteLine($" - {inst}");
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|