using MongoDB.Bson; using MongoDB.Driver; using QuestionsApi.Models; namespace QuestionsApi.Services; public static class Step3SystemBackfillCommand { public const string Confirmation = "BACKFILL_STEP3_SYSTEMS"; public static async Task RunAsync(IConfiguration configuration, string[] args, TextWriter output) { var apply = args.Contains("--apply", StringComparer.Ordinal); var confirmIndex = Array.IndexOf(args, "--confirm"); var confirmation = confirmIndex >= 0 && confirmIndex + 1 < args.Length ? args[confirmIndex + 1] : ""; var database = new MongoDatabaseProvider(configuration).Database; var questions = database.GetCollection("Questions"); var all = await questions.Find(FilterDefinition.Empty).ToListAsync(); var classified = all.Select(question => (Question: question, System: Step3SystemCatalog.Derive(question) ?? "")).ToList(); var changes = classified.Where(item => item.System.Length > 0 && item.Question.Step3System != item.System).ToList(); var unmapped = classified.Count(item => item.System.Length == 0); await output.WriteLineAsync($"Questions requiring Step3System backfill: {changes.Count}; unmapped: {unmapped}."); foreach (var group in classified.Where(item => item.System.Length == 0) .GroupBy(item => $"{item.Question.System} | {item.Question.PerformanceCategory} | {item.Question.Discipline}") .OrderByDescending(group => group.Count()).ThenBy(group => group.Key, StringComparer.Ordinal)) await output.WriteLineAsync($"Unmapped ({group.Count()}): {group.Key}; ids: {string.Join(", ", group.Select(item => item.Question.ExternalId).Take(10))}"); if (!apply) { await output.WriteLineAsync($"Dry run only. Re-run with --apply --confirm {Confirmation}."); return 0; } if (confirmation != Confirmation) { await output.WriteLineAsync($"Refusing migration: --confirm {Confirmation} is required."); return 2; } if (changes.Count > 0) { var writes = changes.Select(item => new UpdateOneModel( Builders.Filter.Eq(question => question.Id, item.Question.Id), Builders.Update.Set(question => question.Step3System, item.System))).ToList(); await questions.BulkWriteAsync(writes, new BulkWriteOptions { IsOrdered = false }); } var remaining = await questions.CountDocumentsAsync(new BsonDocumentFilterDefinition( new BsonDocument("$or", new BsonArray { new BsonDocument(nameof(Question.Step3System), new BsonDocument("$exists", false)), new BsonDocument(nameof(Question.Step3System), "") }))); await output.WriteLineAsync($"Backfilled {changes.Count}; unmapped or empty Step3System: {remaining}."); return 0; } }