fix: Ignore employment overlaps at the same company
Sequential roles at the same company (promotions, transfers) should not be flagged as suspicious overlaps. Only flag overlaps between different companies. - Add IsSameCompany() check before flagging overlaps - Normalize company names to handle variations like "BMW" vs "BMW UK" - Remove common suffixes (Ltd, PLC, Group, etc.) for comparison 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -120,6 +120,15 @@ public sealed class TimelineAnalyserService : ITimelineAnalyserService
|
|||||||
var earlier = sortedEmployment[i];
|
var earlier = sortedEmployment[i];
|
||||||
var later = sortedEmployment[j];
|
var later = sortedEmployment[j];
|
||||||
|
|
||||||
|
// Skip overlaps at the same company (internal promotions/transfers)
|
||||||
|
if (IsSameCompany(earlier.CompanyName, later.CompanyName))
|
||||||
|
{
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Ignoring overlap at same company: {Company1} -> {Company2}",
|
||||||
|
earlier.CompanyName, later.CompanyName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var overlap = CalculateOverlap(earlier, later);
|
var overlap = CalculateOverlap(earlier, later);
|
||||||
|
|
||||||
if (overlap is not null && overlap.Value.Months > AllowedOverlapMonths)
|
if (overlap is not null && overlap.Value.Months > AllowedOverlapMonths)
|
||||||
@@ -143,6 +152,59 @@ public sealed class TimelineAnalyserService : ITimelineAnalyserService
|
|||||||
return overlaps;
|
return overlaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if two company names refer to the same company.
|
||||||
|
/// Handles variations like "BMW" vs "BMW UK" vs "BMW Group".
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsSameCompany(string? company1, string? company2)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(company1) || string.IsNullOrWhiteSpace(company2))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize names for comparison
|
||||||
|
var name1 = NormalizeCompanyName(company1);
|
||||||
|
var name2 = NormalizeCompanyName(company2);
|
||||||
|
|
||||||
|
// Exact match after normalization
|
||||||
|
if (name1.Equals(name2, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if one contains the other (for "BMW" vs "BMW UK" cases)
|
||||||
|
if (name1.Length >= 3 && name2.Length >= 3)
|
||||||
|
{
|
||||||
|
if (name1.StartsWith(name2, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name2.StartsWith(name1, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeCompanyName(string name)
|
||||||
|
{
|
||||||
|
// Remove common suffixes and normalize
|
||||||
|
var normalized = name.Trim();
|
||||||
|
|
||||||
|
string[] suffixes = ["Ltd", "Ltd.", "Limited", "PLC", "Plc", "Inc", "Inc.",
|
||||||
|
"Corporation", "Corp", "Corp.", "UK", "Group", "(UK)", "& Co", "& Co."];
|
||||||
|
|
||||||
|
foreach (var suffix in suffixes)
|
||||||
|
{
|
||||||
|
if (normalized.EndsWith(" " + suffix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
normalized = normalized[..^(suffix.Length + 1)].Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
private static (DateOnly Start, DateOnly End, int Months)? CalculateOverlap(
|
private static (DateOnly Start, DateOnly End, int Months)? CalculateOverlap(
|
||||||
EmploymentEntry earlier,
|
EmploymentEntry earlier,
|
||||||
EmploymentEntry later)
|
EmploymentEntry later)
|
||||||
|
|||||||
Reference in New Issue
Block a user