|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
@@ -7,29 +7,52 @@ namespace JobTrackerApi.Services.JobImport;
|
|
|
|
|
|
|
|
|
|
public static class SkillTagger
|
|
|
|
|
{
|
|
|
|
|
private static readonly (string Tag, Regex Pattern)[] Patterns =
|
|
|
|
|
private static readonly (string Tag, Regex Pattern, int Weight)[] Patterns =
|
|
|
|
|
{
|
|
|
|
|
("C#", new Regex(@"\bC#\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
(".NET", new Regex(@"\b\.NET\b|\bASP\.NET\b|\bDOTNET\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("Python", new Regex(@"\bPython\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("Docker", new Regex(@"\bDocker\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("Azure", new Regex(@"\bAzure\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("AWS", new Regex(@"\bAWS\b|\bAmazon Web Services\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("React", new Regex(@"\bReact\b|\bReact\.js\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("TypeScript", new Regex(@"\bTypeScript\b|\bTS\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("SQL", new Regex(@"\bSQL\b|\bPostgreSQL\b|\bMySQL\b|\bSQLite\b|\bMS\s*SQL\b|\bT-?SQL\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("Kubernetes", new Regex(@"\bKubernetes\b|\bK8s\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)),
|
|
|
|
|
("C#", new Regex(@"\bC#\b|\bcsharp\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 6),
|
|
|
|
|
(".NET", new Regex(@"\b\.NET\b|\bASP\.NET\b|\bDOTNET\b|\bEntity Framework\b|\bEF Core\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 6),
|
|
|
|
|
("Python", new Regex(@"\bPython\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 6),
|
|
|
|
|
("Java", new Regex(@"\bJava\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("JavaScript", new Regex(@"\bJavaScript\b|\bJS\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("TypeScript", new Regex(@"\bTypeScript\b|\bTS\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("React", new Regex(@"\bReact\b|\bReact\.js\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("Node.js", new Regex(@"\bNode\b|\bNode\.js\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("SQL", new Regex(@"\bSQL\b|\bPostgreSQL\b|\bMySQL\b|\bSQLite\b|\bMS\s*SQL\b|\bT-?SQL\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("Docker", new Regex(@"\bDocker\b|\bcontainers?\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("Kubernetes", new Regex(@"\bKubernetes\b|\bK8s\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("Azure", new Regex(@"\bAzure\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("AWS", new Regex(@"\bAWS\b|\bAmazon Web Services\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 5),
|
|
|
|
|
("CI/CD", new Regex(@"\bCI/CD\b|continuous integration|continuous delivery|continuous deployment", RegexOptions.IgnoreCase | RegexOptions.Compiled), 4),
|
|
|
|
|
("REST APIs", new Regex(@"\bREST\b|RESTful|API development|web services", RegexOptions.IgnoreCase | RegexOptions.Compiled), 4),
|
|
|
|
|
("GraphQL", new Regex(@"\bGraphQL\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 4),
|
|
|
|
|
("Testing", new Regex(@"\bunit tests?\b|\bintegration tests?\b|\btesting\b|\bTDD\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 4),
|
|
|
|
|
("Agile", new Regex(@"\bAgile\b|\bScrum\b|\bKanban\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Communication", new Regex(@"communication skills?|communicate effectively|stakeholder management", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Collaboration", new Regex(@"collaborat|cross-functional|team player|work closely with", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Problem Solving", new Regex(@"problem solving|solve complex|analytical|troubleshoot", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Leadership", new Regex(@"leadership|mentor|coaching|leading teams?", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Ownership", new Regex(@"ownership|self-starter|take initiative|proactive", RegexOptions.IgnoreCase | RegexOptions.Compiled), 3),
|
|
|
|
|
("Adaptability", new Regex(@"adaptable|flexible|fast-paced|ambiguity", RegexOptions.IgnoreCase | RegexOptions.Compiled), 2),
|
|
|
|
|
("Attention to Detail", new Regex(@"attention to detail|detail-oriented|quality-focused", RegexOptions.IgnoreCase | RegexOptions.Compiled), 2),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public static string[] Detect(string? description)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(description)) return Array.Empty<string>();
|
|
|
|
|
var tags = new List<string>(capacity: 8);
|
|
|
|
|
foreach (var (tag, pattern) in Patterns)
|
|
|
|
|
|
|
|
|
|
var matches = new List<(string Tag, int Score)>();
|
|
|
|
|
foreach (var (tag, pattern, weight) in Patterns)
|
|
|
|
|
{
|
|
|
|
|
if (pattern.IsMatch(description)) tags.Add(tag);
|
|
|
|
|
if (pattern.IsMatch(description)) matches.Add((tag, weight));
|
|
|
|
|
}
|
|
|
|
|
return tags.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
|
|
|
|
|
|
|
|
|
return matches
|
|
|
|
|
.GroupBy(x => x.Tag, StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
.Select(g => new { Tag = g.Key, Score = g.Max(x => x.Score) })
|
|
|
|
|
.OrderByDescending(x => x.Score)
|
|
|
|
|
.ThenBy(x => x.Tag, StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
.Take(12)
|
|
|
|
|
.Select(x => x.Tag)
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|