< Summary

Information
Class: Orchestrator.Commands.Operations.CollectContext.CollectContextFifaCommand
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Commands/Operations/CollectContext/CollectContextFifaCommand.cs
Line coverage
97%
Covered lines: 88
Uncovered lines: 2
Coverable lines: 90
Total lines: 165
Line coverage: 97.7%
Branch coverage
96%
Covered branches: 25
Total branches: 26
Branch coverage: 96.1%
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%11100%
ExecuteWithSettingsAsync()96.15%262697.44%

File(s)

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

#LineLine coverage
 1using EHonda.KicktippAi.Core;
 2using Microsoft.Extensions.Logging;
 3using Orchestrator.Infrastructure;
 4using Orchestrator.Infrastructure.Factories;
 5using Spectre.Console;
 6using Spectre.Console.Cli;
 7
 8namespace Orchestrator.Commands.Operations.CollectContext;
 9
 10/// <summary>
 11/// Command for uploading WM26 FIFA ranking context and KPI documents.
 12/// </summary>
 13public sealed class CollectContextFifaCommand : AsyncCommand<CollectContextFifaSettings>
 14{
 15    private const string FifaRankingsDocumentName = "fifa-rankings";
 16    private const string FifaRankingsDescription = "WM26 FIFA rankings for all participants, used as KPI context for bon
 17
 18    private readonly IAnsiConsole _console;
 19    private readonly IFirebaseServiceFactory _firebaseServiceFactory;
 20    private readonly IFifaRankingSource _fifaRankingSource;
 21    private readonly ILogger<CollectContextFifaCommand> _logger;
 22
 123    public CollectContextFifaCommand(
 124        IAnsiConsole console,
 125        IFirebaseServiceFactory firebaseServiceFactory,
 126        IFifaRankingSource fifaRankingSource,
 127        ILogger<CollectContextFifaCommand> logger)
 28    {
 129        _console = console;
 130        _firebaseServiceFactory = firebaseServiceFactory;
 131        _fifaRankingSource = fifaRankingSource;
 132        _logger = logger;
 133    }
 34
 35    protected override async Task<int> ExecuteAsync(
 36        CommandContext context,
 37        CollectContextFifaSettings settings,
 38        CancellationToken cancellationToken)
 39    {
 140        return await ExecuteWithSettingsAsync(settings, cancellationToken);
 141    }
 42
 43    internal async Task<int> ExecuteWithSettingsAsync(
 44        CollectContextFifaSettings settings,
 45        CancellationToken cancellationToken = default)
 46    {
 47        try
 48        {
 149            if (string.IsNullOrWhiteSpace(settings.CommunityContext))
 50            {
 051                _console.MarkupLine("[red]Error: Community context is required[/]");
 052                return 1;
 53            }
 54
 155            var communityContext = settings.CommunityContext.Trim();
 156            var competition = CompetitionResolver.ResolveCompetition(settings.Competition, communityContext, communityCo
 157            var repositoryCompetition = CompetitionResolver.ToRepositoryCompetitionArgument(competition);
 158            var collectionDate = DateOnly.FromDateTime(DateTime.UtcNow);
 59
 160            _console.MarkupLine("[green]Collect-context fifa command initialized[/]");
 161            _console.MarkupLine($"[blue]Using community context:[/] [yellow]{Markup.Escape(communityContext)}[/]");
 162            _console.MarkupLine($"[blue]Using competition:[/] [yellow]{Markup.Escape(competition)}[/]");
 63
 164            if (settings.Verbose)
 65            {
 166                _console.MarkupLine("[dim]Verbose mode enabled[/]");
 67            }
 68
 169            if (settings.DryRun)
 70            {
 171                _console.MarkupLine("[magenta]Dry run mode enabled - no changes will be made to database[/]");
 72            }
 73
 174            var source = await _fifaRankingSource.CollectLatestAsync(collectionDate, cancellationToken);
 175            _console.MarkupLine($"[blue]Using FIFA ranking schedule:[/] [yellow]{Markup.Escape(source.ScheduleId)}[/]");
 176            _console.MarkupLine($"[blue]FIFA ranking published at:[/] [yellow]{source.PublicationDateUtc:O}[/]");
 177            _console.MarkupLine($"[blue]Collection date:[/] [yellow]{source.CollectionDate:yyyy-MM-dd}[/]");
 178            _console.MarkupLine($"[blue]Source ranking rows:[/] [yellow]{source.SourceRowCount}[/]");
 179            _console.MarkupLine($"[blue]Mapped WM26 teams:[/] [yellow]{source.MappedTeamCount}[/]");
 80
 181            if (settings.DryRun)
 82            {
 183                foreach (var rankingFile in source.ContextDocuments)
 84                {
 185                    _console.MarkupLine($"[magenta]  Dry run - would save context document:[/] {Markup.Escape(rankingFil
 86                }
 87
 188                _console.MarkupLine($"[magenta]  Dry run - would save KPI document:[/] {FifaRankingsDocumentName}");
 189                _console.MarkupLine($"[magenta]✓ Dry run completed - would have processed {source.ContextDocuments.Count
 190                return 0;
 91            }
 92
 193            var contextRepository = _firebaseServiceFactory.CreateContextRepository(repositoryCompetition);
 194            var kpiRepository = _firebaseServiceFactory.CreateKpiRepository(repositoryCompetition);
 95
 196            var savedContextCount = 0;
 197            var skippedContextCount = 0;
 98
 199            foreach (var rankingFile in source.ContextDocuments)
 100            {
 1101                var existingContextDocument = await contextRepository.GetLatestContextDocumentAsync(
 1102                    rankingFile.DocumentName,
 1103                    communityContext,
 1104                    cancellationToken);
 1105                var contentToSave = FifaRankingCsvUtility.PreserveExistingContentWhenRankingUnchanged(
 1106                    rankingFile.Content,
 1107                    existingContextDocument?.Content);
 1108                var savedVersion = await contextRepository.SaveContextDocumentAsync(
 1109                    rankingFile.DocumentName,
 1110                    contentToSave,
 1111                    communityContext,
 1112                    cancellationToken);
 113
 1114                if (savedVersion.HasValue)
 115                {
 1116                    savedContextCount++;
 1117                    if (settings.Verbose)
 118                    {
 1119                        _console.MarkupLine($"[green]  ✓ Saved {Markup.Escape(rankingFile.DocumentName)} as version {sav
 120                    }
 121                }
 122                else
 123                {
 1124                    skippedContextCount++;
 1125                    if (settings.Verbose)
 126                    {
 1127                        _console.MarkupLine($"[dim]  - Skipped {Markup.Escape(rankingFile.DocumentName)} (content unchan
 128                    }
 129                }
 1130            }
 131
 1132            var existingKpiDocument = await kpiRepository.GetKpiDocumentAsync(
 1133                FifaRankingsDocumentName,
 1134                communityContext,
 1135                cancellationToken);
 1136            var kpiContentToSave = FifaRankingCsvUtility.PreserveExistingContentWhenRankingUnchanged(
 1137                source.KpiContent,
 1138                existingKpiDocument?.Content);
 1139            var savedKpiVersion = await kpiRepository.SaveKpiDocumentAsync(
 1140                FifaRankingsDocumentName,
 1141                kpiContentToSave,
 1142                FifaRankingsDescription,
 1143                communityContext,
 1144                cancellationToken);
 145
 1146            var kpiChanged = existingKpiDocument is null ||
 1147                             !string.Equals(existingKpiDocument.Content, kpiContentToSave, StringComparison.Ordinal);
 148
 1149            _console.MarkupLine("[green]✓ FIFA ranking context collection completed![/]");
 1150            _console.MarkupLine($"[green]  Saved: {savedContextCount} context documents[/]");
 1151            _console.MarkupLine($"[dim]  Skipped: {skippedContextCount} context documents (unchanged)[/]");
 1152            _console.MarkupLine(kpiChanged
 1153                ? $"[green]  KPI document {FifaRankingsDocumentName} saved as version {savedKpiVersion}[/]"
 1154                : $"[dim]  KPI document {FifaRankingsDocumentName} unchanged at version {savedKpiVersion}[/]");
 155
 1156            return 0;
 157        }
 1158        catch (Exception ex)
 159        {
 1160            _logger.LogError(ex, "Error executing collect-context fifa command");
 1161            _console.MarkupLine($"[red]Error:[/] {Markup.Escape(ex.Message)}");
 1162            return 1;
 163        }
 1164    }
 165}