Files
OTSSignsOrchestrator/OTSSignsOrchestrator.Server/Services/AbbreviationService.cs
Matt Batchelder c6d46098dd feat: Implement provisioning pipelines for subscription management
- Add ReactivatePipeline to handle subscription reactivation, including scaling Docker services, health verification, status updates, audit logging, and broadcasting status changes.
- Introduce RotateCredentialsPipeline for OAuth2 credential rotation, managing the deletion of old apps, creation of new ones, credential storage, access verification, and audit logging.
- Create StepRunner to manage job step execution, including lifecycle management and progress broadcasting via SignalR.
- Implement SuspendPipeline for subscription suspension, scaling down services, updating statuses, logging audits, and broadcasting changes.
- Add UpdateScreenLimitPipeline to update Xibo CMS screen limits and record snapshots.
- Introduce XiboFeatureManifests for hardcoded feature ACLs per role.
- Add docker-compose.dev.yml for local development with PostgreSQL setup.
2026-03-18 10:27:26 -04:00

79 lines
2.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using OTSSignsOrchestrator.Server.Data;
namespace OTSSignsOrchestrator.Server.Services;
public class AbbreviationService
{
private static readonly HashSet<string> StopWords = new(StringComparer.OrdinalIgnoreCase)
{
"inc", "llc", "ltd", "co", "corp", "group", "signs", "digital",
"media", "the", "and", "of", "a"
};
private readonly OrchestratorDbContext _db;
private readonly ILogger<AbbreviationService> _logger;
public AbbreviationService(OrchestratorDbContext db, ILogger<AbbreviationService> logger)
{
_db = db;
_logger = logger;
}
public async Task<string> GenerateAsync(string companyName)
{
var words = Regex.Split(companyName.Trim(), @"\s+")
.Select(w => Regex.Replace(w, @"[^a-zA-Z0-9]", ""))
.Where(w => w.Length > 0 && !StopWords.Contains(w))
.ToList();
if (words.Count == 0)
throw new InvalidOperationException(
$"Cannot generate abbreviation from company name '{companyName}' — no usable words after filtering.");
string abbrev;
if (words.Count >= 3)
{
// Take first letter of first 3 words
abbrev = string.Concat(words.Take(3).Select(w => w[0]));
}
else if (words.Count == 2)
{
// First letter of each word + second char of last word
abbrev = $"{words[0][0]}{words[1][0]}{(words[1].Length > 1 ? words[1][1] : words[0][1])}";
}
else
{
// Single word — take up to 3 chars
abbrev = words[0].Length >= 3 ? words[0][..3] : words[0].PadRight(3, 'X');
}
abbrev = Regex.Replace(abbrev.ToUpperInvariant(), @"[^A-Z0-9]", "");
if (abbrev.Length > 3) abbrev = abbrev[..3];
// Check uniqueness
if (!await _db.Customers.AnyAsync(c => c.Abbreviation == abbrev))
{
_logger.LogInformation("Generated abbreviation {Abbrev} for '{CompanyName}'", abbrev, companyName);
return abbrev;
}
// Collision — try suffix 29
var prefix = abbrev[..2];
for (var suffix = 2; suffix <= 9; suffix++)
{
var candidate = $"{prefix}{suffix}";
if (!await _db.Customers.AnyAsync(c => c.Abbreviation == candidate))
{
_logger.LogInformation("Generated abbreviation {Abbrev} (collision resolved) for '{CompanyName}'",
candidate, companyName);
return candidate;
}
}
throw new InvalidOperationException(
$"All abbreviation variants for '{companyName}' are taken ({prefix}2{prefix}9).");
}
}