< Summary

Information
Class: Orchestrator.Commands.Observability.AnalyzeMatch.AnalyzeMatchCommandHelpers
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Observability/AnalyzeMatch/AnalyzeMatchCommandHelpers.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 110
Coverable lines: 110
Total lines: 199
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 44
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CreateLoggerFactory(...)100%210%
ResolveMatchAsync()0%4260%
GetMatchContextDocumentsAsync()0%420200%
GetTeamAbbreviation(...)0%110100%

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
 10internal sealed record AnalyzeMatchContextDocumentInfo(DocumentContext Document, int Version);
 11
 12internal static class AnalyzeMatchCommandHelpers
 13{
 14    public static ILoggerFactory CreateLoggerFactory(bool debug)
 15    {
 016        return LoggerFactory.Create(builder =>
 017        {
 018            builder.AddSimpleConsole(options =>
 019            {
 020                options.SingleLine = true;
 021                options.IncludeScopes = false;
 022                options.ColorBehavior = Microsoft.Extensions.Logging.Console.LoggerColorBehavior.Enabled;
 023            });
 024            builder.SetMinimumLevel(debug ? LogLevel.Information : LogLevel.Error);
 025        });
 26    }
 27
 28    public static async Task<Match?> ResolveMatchAsync(
 29        AnalyzeMatchBaseSettings settings,
 30        IKicktippClient? kicktippClient,
 31        ILogger logger,
 32        string communityContext)
 33    {
 34
 035        if (kicktippClient != null)
 36        {
 37            try
 38            {
 039                var matches = await kicktippClient.GetMatchesWithHistoryAsync(communityContext);
 040                var found = matches.FirstOrDefault(m =>
 041                    m.Match.Matchday == settings.Matchday &&
 042                    string.Equals(m.Match.HomeTeam, settings.HomeTeam, StringComparison.OrdinalIgnoreCase) &&
 043                    string.Equals(m.Match.AwayTeam, settings.AwayTeam, StringComparison.OrdinalIgnoreCase));
 44
 045                if (found != null)
 46                {
 047                    AnsiConsole.MarkupLine("[dim]Using match metadata from Kicktipp schedule[/]");
 048                    return found.Match;
 49                }
 50
 051                logger.LogWarning(
 052                    "Match not found via Kicktipp lookup for community {CommunityContext}, matchday {Matchday}, teams {H
 053                    communityContext,
 054                    settings.Matchday,
 055                    settings.HomeTeam,
 056                    settings.AwayTeam);
 057            }
 058            catch (Exception ex)
 59            {
 060                logger.LogWarning(ex, "Failed to fetch match metadata from Kicktipp; continuing with provided details");
 061            }
 62        }
 63        else
 64        {
 065            logger.LogWarning("Kicktipp client not configured; continuing with provided match details");
 66        }
 67
 068        return new Match(
 069            settings.HomeTeam,
 070            settings.AwayTeam,
 071            default,
 072            settings.Matchday!.Value);
 073    }
 74
 75    public static async Task<List<AnalyzeMatchContextDocumentInfo>> GetMatchContextDocumentsAsync(
 76        IContextRepository contextRepository,
 77        string homeTeam,
 78        string awayTeam,
 79        string communityContext,
 80        bool verbose)
 81    {
 082        var contextDocuments = new List<AnalyzeMatchContextDocumentInfo>();
 083        var homeAbbreviation = GetTeamAbbreviation(homeTeam);
 084        var awayAbbreviation = GetTeamAbbreviation(awayTeam);
 85
 086        var requiredDocuments = new[]
 087        {
 088            "bundesliga-standings.csv",
 089            $"community-rules-{communityContext}.md",
 090            $"recent-history-{homeAbbreviation}.csv",
 091            $"recent-history-{awayAbbreviation}.csv",
 092            $"home-history-{homeAbbreviation}.csv",
 093            $"away-history-{awayAbbreviation}.csv",
 094            $"head-to-head-{homeAbbreviation}-vs-{awayAbbreviation}.csv"
 095        };
 96
 097        var optionalDocuments = new[]
 098        {
 099            $"{homeAbbreviation}-transfers.csv",
 0100            $"{awayAbbreviation}-transfers.csv"
 0101        };
 102
 0103        if (verbose)
 104        {
 0105            AnsiConsole.MarkupLine($"[dim]Looking for {requiredDocuments.Length} required context documents in database[
 106        }
 107
 0108        foreach (var documentName in requiredDocuments)
 109        {
 0110            var contextDoc = await contextRepository.GetLatestContextDocumentAsync(documentName, communityContext);
 0111            if (contextDoc != null)
 112            {
 0113                contextDocuments.Add(new AnalyzeMatchContextDocumentInfo(new DocumentContext(contextDoc.DocumentName, co
 114
 0115                if (verbose)
 116                {
 0117                    AnsiConsole.MarkupLine($"[dim]  ✓ Retrieved {documentName} (version {contextDoc.Version})[/]");
 118                }
 119            }
 0120            else if (verbose)
 121            {
 0122                AnsiConsole.MarkupLine($"[dim]  ✗ Missing {documentName}[/]");
 123            }
 0124        }
 125
 0126        foreach (var documentName in optionalDocuments)
 127        {
 128            try
 129            {
 0130                var contextDoc = await contextRepository.GetLatestContextDocumentAsync(documentName, communityContext);
 0131                if (contextDoc != null)
 132                {
 0133                    contextDocuments.Add(new AnalyzeMatchContextDocumentInfo(new DocumentContext(contextDoc.DocumentName
 134
 0135                    if (verbose)
 136                    {
 0137                        AnsiConsole.MarkupLine($"[dim]  ✓ Retrieved optional {documentName} (version {contextDoc.Version
 138                    }
 139                }
 0140                else if (verbose)
 141                {
 0142                    AnsiConsole.MarkupLine($"[dim]  · Missing optional {documentName}[/]");
 143                }
 0144            }
 0145            catch (Exception ex)
 146            {
 0147                if (verbose)
 148                {
 0149                    AnsiConsole.MarkupLine($"[dim]  · Failed optional {documentName}: {Markup.Escape(ex.Message)}[/]");
 150                }
 0151            }
 0152        }
 153
 0154        return contextDocuments;
 0155    }
 156
 157    private static string GetTeamAbbreviation(string teamName)
 158    {
 0159        var abbreviations = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
 0160        {
 0161            { "1. FC Heidenheim 1846", "fch" },
 0162            { "1. FC Köln", "fck" },
 0163            { "1. FC Union Berlin", "fcu" },
 0164            { "1899 Hoffenheim", "tsg" },
 0165            { "Bayer 04 Leverkusen", "b04" },
 0166            { "Bor. Mönchengladbach", "bmg" },
 0167            { "Borussia Dortmund", "bvb" },
 0168            { "Eintracht Frankfurt", "sge" },
 0169            { "FC Augsburg", "fca" },
 0170            { "FC Bayern München", "fcb" },
 0171            { "FC St. Pauli", "fcs" },
 0172            { "FSV Mainz 05", "m05" },
 0173            { "Hamburger SV", "hsv" },
 0174            { "RB Leipzig", "rbl" },
 0175            { "SC Freiburg", "scf" },
 0176            { "VfB Stuttgart", "vfb" },
 0177            { "VfL Wolfsburg", "wob" },
 0178            { "Werder Bremen", "svw" }
 0179        };
 180
 0181        if (abbreviations.TryGetValue(teamName, out var abbreviation))
 182        {
 0183            return abbreviation;
 184        }
 185
 0186        var words = teamName.Split(' ', StringSplitOptions.RemoveEmptyEntries);
 0187        var builder = new StringBuilder();
 188
 0189        foreach (var word in words.Take(3))
 190        {
 0191            if (word.Length > 0 && char.IsLetter(word[0]))
 192            {
 0193                builder.Append(char.ToLowerInvariant(word[0]));
 194            }
 195        }
 196
 0197        return builder.Length > 0 ? builder.ToString() : "unknown";
 198    }
 199}