< Summary

Information
Class: Orchestrator.Commands.Operations.CollectContext.CollectContextKicktippCommand
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Operations/CollectContext/CollectContextKicktippCommand.cs
Line coverage
100%
Covered lines: 94
Uncovered lines: 0
Coverable lines: 94
Total lines: 208
Line coverage: 100%
Branch coverage
100%
Covered branches: 36
Total branches: 36
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ExecuteAsync()100%66100%
ExecuteKicktippContextCollection()100%2626100%
IsHistoryDocument(...)100%44100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Operations/CollectContext/CollectContextKicktippCommand.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using Spectre.Console.Cli;
 3using Spectre.Console;
 4using EHonda.KicktippAi.Core;
 5using Orchestrator.Infrastructure.Factories;
 6
 7namespace Orchestrator.Commands.Operations.CollectContext;
 8
 9/// <summary>
 10/// Command for collecting Kicktipp context documents and storing them in the database.
 11/// </summary>
 12public class CollectContextKicktippCommand : AsyncCommand<CollectContextKicktippSettings>
 13{
 14    private readonly IAnsiConsole _console;
 15    private readonly IFirebaseServiceFactory _firebaseServiceFactory;
 16    private readonly IKicktippClientFactory _kicktippClientFactory;
 17    private readonly IContextProviderFactory _contextProviderFactory;
 18    private readonly ILogger<CollectContextKicktippCommand> _logger;
 19
 120    public CollectContextKicktippCommand(
 121        IAnsiConsole console,
 122        IFirebaseServiceFactory firebaseServiceFactory,
 123        IKicktippClientFactory kicktippClientFactory,
 124        IContextProviderFactory contextProviderFactory,
 125        ILogger<CollectContextKicktippCommand> logger)
 26    {
 127        _console = console;
 128        _firebaseServiceFactory = firebaseServiceFactory;
 129        _kicktippClientFactory = kicktippClientFactory;
 130        _contextProviderFactory = contextProviderFactory;
 131        _logger = logger;
 132    }
 33
 34    public override async Task<int> ExecuteAsync(CommandContext context, CollectContextKicktippSettings settings)
 35    {
 36
 37        try
 38        {
 39            // Validate settings
 140            if (string.IsNullOrWhiteSpace(settings.CommunityContext))
 41            {
 142                _console.MarkupLine("[red]Error: Community context is required[/]");
 143                return 1;
 44            }
 45
 146            _console.MarkupLine($"[green]Collect-context kicktipp command initialized[/]");
 47
 148            if (settings.Verbose)
 49            {
 150                _console.MarkupLine("[dim]Verbose mode enabled[/]");
 51            }
 52
 153            if (settings.DryRun)
 54            {
 155                _console.MarkupLine("[magenta]Dry run mode enabled - no changes will be made to database[/]");
 56            }
 57
 58            // Execute the context collection workflow
 159            await ExecuteKicktippContextCollection(settings);
 60
 161            return 0;
 62        }
 163        catch (Exception ex)
 64        {
 165            _logger.LogError(ex, "Error executing collect-context kicktipp command");
 166            _console.MarkupLine($"[red]Error:[/] {ex.Message}");
 167            return 1;
 68        }
 169    }
 70
 71    private async Task ExecuteKicktippContextCollection(CollectContextKicktippSettings settings)
 72    {
 73        // Create services using factories (factories handle env var loading)
 174        var kicktippClient = _kicktippClientFactory.CreateClient();
 175        var contextRepository = _firebaseServiceFactory.CreateContextRepository();
 76
 77        // Create context provider using factory
 178        var contextProvider = _contextProviderFactory.CreateKicktippContextProvider(
 179            kicktippClient, settings.CommunityContext, settings.CommunityContext);
 80
 181        _console.MarkupLine($"[blue]Using community context:[/] [yellow]{settings.CommunityContext}[/]");
 182        _console.MarkupLine("[blue]Getting current matchday matches...[/]");
 83
 84        // Step 1: Get current matchday matches
 185        var matchesWithHistory = await kicktippClient.GetMatchesWithHistoryAsync(settings.CommunityContext);
 86
 187        if (!matchesWithHistory.Any())
 88        {
 189            _console.MarkupLine("[yellow]No matches found for current matchday[/]");
 190            return;
 91        }
 92
 193        _console.MarkupLine($"[green]Found {matchesWithHistory.Count} matches for current matchday[/]");
 94
 95        // Step 2: Collect all unique context documents for all matches
 196        var allContextDocuments = new Dictionary<string, string>(); // documentName -> content
 97
 198        foreach (var matchWithHistory in matchesWithHistory)
 99        {
 1100            var match = matchWithHistory.Match;
 1101            _console.MarkupLine($"[cyan]Collecting context for:[/] {match.HomeTeam} vs {match.AwayTeam}");
 102
 103            try
 104            {
 105                // Get context for this specific match
 1106                await foreach (var contextDoc in contextProvider.GetMatchContextAsync(match.HomeTeam, match.AwayTeam))
 107                {
 108                    // Use the document name as key to avoid duplicates
 1109                    if (!allContextDocuments.ContainsKey(contextDoc.Name))
 110                    {
 1111                        allContextDocuments[contextDoc.Name] = contextDoc.Content;
 112
 1113                        if (settings.Verbose)
 114                        {
 1115                            _console.MarkupLine($"[dim]  Collected context document: {contextDoc.Name}[/]");
 116                        }
 117                    }
 118                }
 1119            }
 1120            catch (Exception ex)
 121            {
 1122                _logger.LogError(ex, "Failed to collect context for match {HomeTeam} vs {AwayTeam}", match.HomeTeam, mat
 1123                _console.MarkupLine($"[red]  ✗ Failed to collect context: {ex.Message}[/]");
 1124            }
 1125        }
 126
 1127        _console.MarkupLine($"[green]Collected {allContextDocuments.Count} unique context documents[/]");
 128
 129        // Step 3: Save context documents to database
 1130        var savedCount = 0;
 1131        var skippedCount = 0;
 1132        var currentDate = DateTime.Now.ToString("yyyy-MM-dd");
 133
 1134        foreach (var (documentName, content) in allContextDocuments)
 135        {
 136            try
 137            {
 1138                if (settings.DryRun)
 139                {
 1140                    _console.MarkupLine($"[magenta]  Dry run - would save:[/] {documentName}");
 1141                    continue;
 142                }
 143
 144                // Check if this is a history document that needs Data_Collected_At column
 1145                string finalContent = content;
 1146                if (IsHistoryDocument(documentName))
 147                {
 148                    // Get the previous version to compare against
 1149                    var previousDocument = await contextRepository.GetLatestContextDocumentAsync(documentName, settings.
 1150                    var previousContent = previousDocument?.Content;
 151
 152                    // Add Data_Collected_At column with current date for new matches
 1153                    finalContent = HistoryCsvUtility.AddDataCollectedAtColumn(content, previousContent, currentDate);
 154
 1155                    if (settings.Verbose)
 156                    {
 1157                        _console.MarkupLine($"[dim]  Added Data_Collected_At column to {documentName}[/]");
 158                    }
 159                }
 160
 1161                var savedVersion = await contextRepository.SaveContextDocumentAsync(
 1162                    documentName,
 1163                    finalContent,
 1164                    settings.CommunityContext);
 165
 1166                if (savedVersion.HasValue)
 167                {
 1168                    savedCount++;
 1169                    if (settings.Verbose)
 170                    {
 1171                        _console.MarkupLine($"[green]  ✓ Saved {documentName} as version {savedVersion.Value}[/]");
 172                    }
 173                }
 174                else
 175                {
 1176                    skippedCount++;
 1177                    if (settings.Verbose)
 178                    {
 1179                        _console.MarkupLine($"[dim]  - Skipped {documentName} (content unchanged)[/]");
 180                    }
 181                }
 1182            }
 1183            catch (Exception ex)
 184            {
 1185                _logger.LogError(ex, "Failed to save context document {DocumentName}", documentName);
 1186                _console.MarkupLine($"[red]  ✗ Failed to save {documentName}: {ex.Message}[/]");
 1187            }
 1188        }
 189
 1190        if (settings.DryRun)
 191        {
 1192            _console.MarkupLine($"[magenta]✓ Dry run completed - would have processed {allContextDocuments.Count} docume
 193        }
 194        else
 195        {
 1196            _console.MarkupLine($"[green]✓ Context collection completed![/]");
 1197            _console.MarkupLine($"[green]  Saved: {savedCount} documents[/]");
 1198            _console.MarkupLine($"[dim]  Skipped: {skippedCount} documents (unchanged)[/]");
 199        }
 1200    }
 201
 202    private static bool IsHistoryDocument(string documentName)
 203    {
 1204        return documentName.StartsWith("recent-history-", StringComparison.OrdinalIgnoreCase) ||
 1205               documentName.StartsWith("home-history-", StringComparison.OrdinalIgnoreCase) ||
 1206               documentName.StartsWith("away-history-", StringComparison.OrdinalIgnoreCase);
 207    }
 208}