< Summary

Information
Class: EHonda.KicktippAi.Core.MatchContextDocumentCatalog
Assembly: EHonda.KicktippAi.Core
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Core/MatchContextDocumentCatalog.cs
Line coverage
100%
Covered lines: 121
Uncovered lines: 0
Coverable lines: 121
Total lines: 226
Line coverage: 100%
Branch coverage
97%
Covered branches: 35
Total branches: 36
Branch coverage: 97.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.cctor()100%11100%
ForMatch(...)100%1414100%
ForCommunity(...)100%22100%
GetStandingsDocumentName(...)100%22100%
GetStandingsDocumentBaseName(...)50%22100%
GetFifaRankingDocumentName(...)100%11100%
GetLineupDocumentName(...)100%11100%
GetTeamAbbreviation(...)100%22100%
ResolvePolicy(...)100%44100%
SlugifyTeamName(...)100%1010100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Core/MatchContextDocumentCatalog.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.RegularExpressions;
 3
 4namespace EHonda.KicktippAi.Core;
 5
 6public sealed record MatchContextDocumentSelection(
 7    IReadOnlyList<string> RequiredDocumentNames,
 8    IReadOnlyList<string> OptionalDocumentNames);
 9
 10public static class MatchContextDocumentCatalog
 11{
 112    private sealed record MatchContextDocumentPolicy(
 113        bool IncludeCommunityRules,
 114        bool IncludeRecentHistory,
 115        bool IncludeFifaRankings,
 116        bool IncludeLineups,
 117        bool IncludeHomeAwayHistory,
 118        bool IncludeHeadToHead,
 119        bool IncludeTransfers);
 20
 121    private static readonly MatchContextDocumentPolicy BundesligaPolicy = new(
 122        IncludeCommunityRules: true,
 123        IncludeRecentHistory: true,
 124        IncludeFifaRankings: false,
 125        IncludeLineups: false,
 126        IncludeHomeAwayHistory: true,
 127        IncludeHeadToHead: true,
 128        IncludeTransfers: true);
 29
 130    private static readonly MatchContextDocumentPolicy WorldCup2026Policy = new(
 131        IncludeCommunityRules: true,
 132        IncludeRecentHistory: true,
 133        IncludeFifaRankings: true,
 134        IncludeLineups: true,
 135        IncludeHomeAwayHistory: false,
 136        IncludeHeadToHead: false,
 137        IncludeTransfers: false);
 38
 139    private static readonly IReadOnlyDictionary<string, MatchContextDocumentPolicy> CommunityPolicies =
 140        new Dictionary<string, MatchContextDocumentPolicy>(StringComparer.OrdinalIgnoreCase)
 141        {
 142            ["ehonda-dev-wm26"] = WorldCup2026Policy
 143        };
 44
 145    private static readonly IReadOnlyDictionary<string, string> TeamAbbreviations =
 146        new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
 147        {
 148            { "1. FC Heidenheim 1846", "fch" },
 149            { "1. FC Köln", "fck" },
 150            { "1. FC Union Berlin", "fcu" },
 151            { "1899 Hoffenheim", "tsg" },
 152            { "Bayer 04 Leverkusen", "b04" },
 153            { "Bor. Mönchengladbach", "bmg" },
 154            { "Borussia Dortmund", "bvb" },
 155            { "Eintracht Frankfurt", "sge" },
 156            { "FC Augsburg", "fca" },
 157            { "FC Bayern München", "fcb" },
 158            { "FC St. Pauli", "fcs" },
 159            { "FSV Mainz 05", "m05" },
 160            { "Hamburger SV", "hsv" },
 161            { "RB Leipzig", "rbl" },
 162            { "SC Freiburg", "scf" },
 163            { "VfB Stuttgart", "vfb" },
 164            { "VfL Wolfsburg", "wob" },
 165            { "Werder Bremen", "svw" }
 166        };
 67
 68    public static MatchContextDocumentSelection ForMatch(
 69        string homeTeam,
 70        string awayTeam,
 71        string communityContext,
 72        string? competition = null)
 73    {
 174        ArgumentException.ThrowIfNullOrWhiteSpace(homeTeam);
 175        ArgumentException.ThrowIfNullOrWhiteSpace(awayTeam);
 176        ArgumentException.ThrowIfNullOrWhiteSpace(communityContext);
 77
 178        var homeAbbreviation = GetTeamAbbreviation(homeTeam);
 179        var awayAbbreviation = GetTeamAbbreviation(awayTeam);
 180        var standingsDocumentName = GetStandingsDocumentName(competition);
 181        var policy = ResolvePolicy(communityContext, competition);
 82
 183        var requiredDocuments = new List<string> { standingsDocumentName };
 184        if (policy.IncludeCommunityRules)
 85        {
 186            requiredDocuments.Add($"community-rules-{communityContext}.md");
 87        }
 88
 189        if (policy.IncludeRecentHistory)
 90        {
 191            requiredDocuments.Add($"recent-history-{homeAbbreviation}.csv");
 192            requiredDocuments.Add($"recent-history-{awayAbbreviation}.csv");
 93        }
 94
 195        if (policy.IncludeFifaRankings)
 96        {
 197            requiredDocuments.Add(GetFifaRankingDocumentName(homeTeam));
 198            requiredDocuments.Add(GetFifaRankingDocumentName(awayTeam));
 99        }
 100
 1101        if (policy.IncludeLineups)
 102        {
 1103            requiredDocuments.Add(GetLineupDocumentName(homeTeam));
 1104            requiredDocuments.Add(GetLineupDocumentName(awayTeam));
 105        }
 106
 1107        if (policy.IncludeHomeAwayHistory)
 108        {
 1109            requiredDocuments.Add($"home-history-{homeAbbreviation}.csv");
 1110            requiredDocuments.Add($"away-history-{awayAbbreviation}.csv");
 111        }
 112
 1113        if (policy.IncludeHeadToHead)
 114        {
 1115            requiredDocuments.Add($"head-to-head-{homeAbbreviation}-vs-{awayAbbreviation}.csv");
 116        }
 117
 1118        var optionalDocuments = policy.IncludeTransfers
 1119            ? new List<string>
 1120            {
 1121                $"{homeAbbreviation}-transfers.csv",
 1122                $"{awayAbbreviation}-transfers.csv"
 1123            }
 1124            : [];
 125
 1126        return new MatchContextDocumentSelection(requiredDocuments, optionalDocuments);
 127    }
 128
 129    public static MatchContextDocumentSelection ForCommunity(
 130        string communityContext,
 131        string? competition = null)
 132    {
 1133        ArgumentException.ThrowIfNullOrWhiteSpace(communityContext);
 134
 1135        var standingsDocumentName = GetStandingsDocumentName(competition);
 1136        var policy = ResolvePolicy(communityContext, competition);
 1137        var requiredDocuments = new List<string> { standingsDocumentName };
 138
 1139        if (policy.IncludeCommunityRules)
 140        {
 1141            requiredDocuments.Add($"community-rules-{communityContext}.md");
 142        }
 143
 1144        return new MatchContextDocumentSelection(requiredDocuments, []);
 145    }
 146
 147    public static string GetStandingsDocumentName(string? competition = null)
 148    {
 1149        return string.Equals(competition, CompetitionIds.FifaWorldCup2026, StringComparison.OrdinalIgnoreCase)
 1150            ? "fifa-world-cup-2026-standings.csv"
 1151            : "bundesliga-standings.csv";
 152    }
 153
 154    public static string GetStandingsDocumentBaseName(string? competition = null)
 155    {
 1156        var documentName = GetStandingsDocumentName(competition);
 1157        return documentName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)
 1158            ? documentName[..^4]
 1159            : documentName;
 160    }
 161
 162    public static string GetFifaRankingDocumentName(string teamName)
 163    {
 1164        ArgumentException.ThrowIfNullOrWhiteSpace(teamName);
 165
 1166        return $"fifa-ranking-{GetTeamAbbreviation(teamName)}.csv";
 167    }
 168
 169    public static string GetLineupDocumentName(string teamName)
 170    {
 1171        ArgumentException.ThrowIfNullOrWhiteSpace(teamName);
 172
 1173        return $"lineup-{GetTeamAbbreviation(teamName)}.csv";
 174    }
 175
 176    public static string GetTeamAbbreviation(string teamName)
 177    {
 1178        ArgumentException.ThrowIfNullOrWhiteSpace(teamName);
 179
 1180        if (TeamAbbreviations.TryGetValue(teamName, out var abbreviation))
 181        {
 1182            return abbreviation;
 183        }
 184
 1185        return SlugifyTeamName(teamName);
 186    }
 187
 188    private static MatchContextDocumentPolicy ResolvePolicy(string communityContext, string? competition)
 189    {
 1190        if (CommunityPolicies.TryGetValue(communityContext, out var communityPolicy))
 191        {
 1192            return communityPolicy;
 193        }
 194
 1195        return string.Equals(competition, CompetitionIds.FifaWorldCup2026, StringComparison.OrdinalIgnoreCase)
 1196            ? WorldCup2026Policy
 1197            : BundesligaPolicy;
 198    }
 199
 200    private static string SlugifyTeamName(string teamName)
 201    {
 1202        var normalized = teamName.Normalize(NormalizationForm.FormD);
 1203        var builder = new StringBuilder();
 204
 1205        foreach (var character in normalized)
 206        {
 1207            var category = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(character);
 1208            if (category == System.Globalization.UnicodeCategory.NonSpacingMark)
 209            {
 210                continue;
 211            }
 212
 1213            if (char.IsLetterOrDigit(character))
 214            {
 1215                builder.Append(char.ToLowerInvariant(character));
 216            }
 1217            else if (builder.Length > 0 && builder[^1] != '-')
 218            {
 1219                builder.Append('-');
 220            }
 221        }
 222
 1223        var slug = Regex.Replace(builder.ToString().Trim('-'), "-{2,}", "-");
 1224        return string.IsNullOrWhiteSpace(slug) ? "unknown" : slug;
 225    }
 226}