< Summary

Information
Class: Orchestrator.Commands.Observability.AnalyzeMatch.AnalyzeMatchContextDocumentInfo
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/AnalyzeMatch/AnalyzeMatchCommandHelpers.cs
Line coverage
100%
Covered lines: 1
Uncovered lines: 0
Coverable lines: 1
Total lines: 201
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Document()100%11100%
set_Document(...)100%210%
get_Version()100%11100%
set_Version(...)100%210%
.ctor(...)100%210%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/AnalyzeMatch/AnalyzeMatchCommandHelpers.cs

#LineLine coverage
 1using System.Text;
 2using EHonda.KicktippAi.Core;
 3using Microsoft.Extensions.Logging;
 4using Spectre.Console;
 5using FirebaseAdapter;
 6using KicktippIntegration;
 7
 8namespace Orchestrator.Commands.Observability.AnalyzeMatch;
 9
 110internal sealed record AnalyzeMatchContextDocumentInfo(DocumentContext Document, int Version);
 11
 12internal static class AnalyzeMatchCommandHelpers
 13{
 14    public static ILoggerFactory CreateLoggerFactory(bool debug)
 15    {
 16        return LoggerFactory.Create(builder =>
 17        {
 18            builder.AddSimpleConsole(options =>
 19            {
 20                options.SingleLine = true;
 21                options.IncludeScopes = false;
 22                options.ColorBehavior = Microsoft.Extensions.Logging.Console.LoggerColorBehavior.Enabled;
 23            });
 24            builder.SetMinimumLevel(debug ? LogLevel.Information : LogLevel.Error);
 25        });
 26    }
 27
 28    public static async Task<Match?> ResolveMatchAsync(
 29        AnalyzeMatchBaseSettings settings,
 30        IKicktippClient? kicktippClient,
 31        ILogger logger,
 32        string communityContext,
 33        IAnsiConsole console)
 34    {
 35
 36        if (kicktippClient != null)
 37        {
 38            try
 39            {
 40                var matches = await kicktippClient.GetMatchesWithHistoryAsync(communityContext);
 41                var found = matches.FirstOrDefault(m =>
 42                    m.Match.Matchday == settings.Matchday &&
 43                    string.Equals(m.Match.HomeTeam, settings.HomeTeam, StringComparison.OrdinalIgnoreCase) &&
 44                    string.Equals(m.Match.AwayTeam, settings.AwayTeam, StringComparison.OrdinalIgnoreCase));
 45
 46                if (found != null)
 47                {
 48                    console.MarkupLine("[dim]Using match metadata from Kicktipp schedule[/]");
 49                    return found.Match;
 50                }
 51
 52                logger.LogWarning(
 53                    "Match not found via Kicktipp lookup for community {CommunityContext}, matchday {Matchday}, teams {H
 54                    communityContext,
 55                    settings.Matchday,
 56                    settings.HomeTeam,
 57                    settings.AwayTeam);
 58            }
 59            catch (Exception ex)
 60            {
 61                logger.LogWarning(ex, "Failed to fetch match metadata from Kicktipp; continuing with provided details");
 62            }
 63        }
 64        else
 65        {
 66            logger.LogWarning("Kicktipp client not configured; continuing with provided match details");
 67        }
 68
 69        return new Match(
 70            settings.HomeTeam,
 71            settings.AwayTeam,
 72            default,
 73            settings.Matchday!.Value);
 74    }
 75
 76    public static async Task<List<AnalyzeMatchContextDocumentInfo>> GetMatchContextDocumentsAsync(
 77        IContextRepository contextRepository,
 78        string homeTeam,
 79        string awayTeam,
 80        string communityContext,
 81        bool verbose,
 82        IAnsiConsole console)
 83    {
 84        var contextDocuments = new List<AnalyzeMatchContextDocumentInfo>();
 85        var homeAbbreviation = GetTeamAbbreviation(homeTeam);
 86        var awayAbbreviation = GetTeamAbbreviation(awayTeam);
 87
 88        var requiredDocuments = new[]
 89        {
 90            "bundesliga-standings.csv",
 91            $"community-rules-{communityContext}.md",
 92            $"recent-history-{homeAbbreviation}.csv",
 93            $"recent-history-{awayAbbreviation}.csv",
 94            $"home-history-{homeAbbreviation}.csv",
 95            $"away-history-{awayAbbreviation}.csv",
 96            $"head-to-head-{homeAbbreviation}-vs-{awayAbbreviation}.csv"
 97        };
 98
 99        var optionalDocuments = new[]
 100        {
 101            $"{homeAbbreviation}-transfers.csv",
 102            $"{awayAbbreviation}-transfers.csv"
 103        };
 104
 105        if (verbose)
 106        {
 107            console.MarkupLine($"[dim]Looking for {requiredDocuments.Length} required context documents in database[/]")
 108        }
 109
 110        foreach (var documentName in requiredDocuments)
 111        {
 112            var contextDoc = await contextRepository.GetLatestContextDocumentAsync(documentName, communityContext);
 113            if (contextDoc != null)
 114            {
 115                contextDocuments.Add(new AnalyzeMatchContextDocumentInfo(new DocumentContext(contextDoc.DocumentName, co
 116
 117                if (verbose)
 118                {
 119                    console.MarkupLine($"[dim]  ✓ Retrieved {documentName} (version {contextDoc.Version})[/]");
 120                }
 121            }
 122            else if (verbose)
 123            {
 124                console.MarkupLine($"[dim]  ✗ Missing {documentName}[/]");
 125            }
 126        }
 127
 128        foreach (var documentName in optionalDocuments)
 129        {
 130            try
 131            {
 132                var contextDoc = await contextRepository.GetLatestContextDocumentAsync(documentName, communityContext);
 133                if (contextDoc != null)
 134                {
 135                    contextDocuments.Add(new AnalyzeMatchContextDocumentInfo(new DocumentContext(contextDoc.DocumentName
 136
 137                    if (verbose)
 138                    {
 139                        console.MarkupLine($"[dim]  ✓ Retrieved optional {documentName} (version {contextDoc.Version})[/
 140                    }
 141                }
 142                else if (verbose)
 143                {
 144                    console.MarkupLine($"[dim]  · Missing optional {documentName}[/]");
 145                }
 146            }
 147            catch (Exception ex)
 148            {
 149                if (verbose)
 150                {
 151                    console.MarkupLine($"[dim]  · Failed optional {documentName}: {Markup.Escape(ex.Message)}[/]");
 152                }
 153            }
 154        }
 155
 156        return contextDocuments;
 157    }
 158
 159    private static string GetTeamAbbreviation(string teamName)
 160    {
 161        var abbreviations = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
 162        {
 163            { "1. FC Heidenheim 1846", "fch" },
 164            { "1. FC Köln", "fck" },
 165            { "1. FC Union Berlin", "fcu" },
 166            { "1899 Hoffenheim", "tsg" },
 167            { "Bayer 04 Leverkusen", "b04" },
 168            { "Bor. Mönchengladbach", "bmg" },
 169            { "Borussia Dortmund", "bvb" },
 170            { "Eintracht Frankfurt", "sge" },
 171            { "FC Augsburg", "fca" },
 172            { "FC Bayern München", "fcb" },
 173            { "FC St. Pauli", "fcs" },
 174            { "FSV Mainz 05", "m05" },
 175            { "Hamburger SV", "hsv" },
 176            { "RB Leipzig", "rbl" },
 177            { "SC Freiburg", "scf" },
 178            { "VfB Stuttgart", "vfb" },
 179            { "VfL Wolfsburg", "wob" },
 180            { "Werder Bremen", "svw" }
 181        };
 182
 183        if (abbreviations.TryGetValue(teamName, out var abbreviation))
 184        {
 185            return abbreviation;
 186        }
 187
 188        var words = teamName.Split(' ', StringSplitOptions.RemoveEmptyEntries);
 189        var builder = new StringBuilder();
 190
 191        foreach (var word in words.Take(3))
 192        {
 193            if (word.Length > 0 && char.IsLetter(word[0]))
 194            {
 195                builder.Append(char.ToLowerInvariant(word[0]));
 196            }
 197        }
 198
 199        return builder.Length > 0 ? builder.ToString() : "unknown";
 200    }
 201}