mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
feat: Prompt to manually link unmatched platforms in match-platforms
After automatic matching, interactively let the operator search the
local platform catalog, create a new platform, skip, or quit ('q')
for any platform that couldn't be matched automatically. Skipped
entirely when stdin is redirected to avoid hanging non-interactive runs.
This commit is contained in:
@@ -38,6 +38,8 @@ public class PlatformMatcher
|
|||||||
int matched = 0;
|
int matched = 0;
|
||||||
int needsReview = 0;
|
int needsReview = 0;
|
||||||
|
|
||||||
|
var unresolved = new List<IgdbPlatform>();
|
||||||
|
|
||||||
foreach(IgdbPlatform igdbPlatform in pending)
|
foreach(IgdbPlatform igdbPlatform in pending)
|
||||||
{
|
{
|
||||||
string normalizedInput = Normalize(igdbPlatform.Name);
|
string normalizedInput = Normalize(igdbPlatform.Name);
|
||||||
@@ -105,15 +107,144 @@ public class PlatformMatcher
|
|||||||
if(match != null)
|
if(match != null)
|
||||||
matched++;
|
matched++;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
needsReview++;
|
needsReview++;
|
||||||
|
unresolved.Add(igdbPlatform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!dryRun)
|
if(!dryRun)
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
if(!dryRun && unresolved.Count > 0)
|
||||||
|
{
|
||||||
|
if(Console.IsInputRedirected)
|
||||||
|
Console.WriteLine(
|
||||||
|
$"\n{unresolved.Count} platform(s) could not be automatically matched. Run interactively (not redirected) to link them manually.");
|
||||||
|
else
|
||||||
|
(matched, needsReview) = await ResolveInteractivelyAsync(context, platforms, unresolved, matched,
|
||||||
|
needsReview);
|
||||||
|
}
|
||||||
|
|
||||||
return (matched, needsReview);
|
return (matched, needsReview);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Walks the platforms that automatic matching could not resolve, one at a time, letting the
|
||||||
|
/// operator search the existing local catalog, create a brand-new <see cref="SoftwarePlatform" />, skip the
|
||||||
|
/// current one, or quit (leaving any remaining platforms as <see cref="IgdbMatchStatus.NeedsReview" /> for a
|
||||||
|
/// later run).</summary>
|
||||||
|
async Task<(int matched, int needsReview)> ResolveInteractivelyAsync(MarechaiContext context,
|
||||||
|
List<SoftwarePlatform> platforms, List<IgdbPlatform> unresolved, int matched, int needsReview)
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
$"\n{unresolved.Count} platform(s) could not be automatically matched. Resolve them now, or press 'q' at any prompt to stop and leave the rest unresolved.");
|
||||||
|
|
||||||
|
foreach(IgdbPlatform igdbPlatform in unresolved)
|
||||||
|
{
|
||||||
|
(SoftwarePlatform resolved, bool quit) = PromptForPlatform(igdbPlatform, platforms);
|
||||||
|
|
||||||
|
if(quit)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" Quitting; remaining platforms left as \"needs review\".");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resolved == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(resolved.Id == 0)
|
||||||
|
{
|
||||||
|
context.SoftwarePlatforms.Add(resolved);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
platforms.Add(resolved);
|
||||||
|
Console.WriteLine($" Created new platform \"{resolved.Name}\" (id {resolved.Id}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
igdbPlatform.SoftwarePlatformId = resolved.Id;
|
||||||
|
igdbPlatform.MatchStatus = IgdbMatchStatus.Matched;
|
||||||
|
igdbPlatform.MatchType = "manual";
|
||||||
|
igdbPlatform.MatchedOn = DateTime.UtcNow;
|
||||||
|
|
||||||
|
matched++;
|
||||||
|
needsReview--;
|
||||||
|
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
Console.WriteLine($" Linked \"{igdbPlatform.Name}\" to \"{resolved.Name}\" (id {resolved.Id}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (matched, needsReview);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Prompts for a single platform. Returns <c>(null, true)</c> on quit, <c>(null, false)</c> on
|
||||||
|
/// skip, or the chosen/newly-built <see cref="SoftwarePlatform" /> (unsaved if newly built, signaled by
|
||||||
|
/// <see cref="SoftwarePlatform.Id" /> being <c>0</c>) otherwise.</summary>
|
||||||
|
static (SoftwarePlatform platform, bool quit) PromptForPlatform(IgdbPlatform igdbPlatform,
|
||||||
|
List<SoftwarePlatform> platforms)
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine($"Unmatched IGDB platform: \"{igdbPlatform.Name}\" (igdb id {igdbPlatform.Id}).");
|
||||||
|
Console.Write(
|
||||||
|
" Enter part of the local platform name to search, 'n' to create a new platform with this name, 's' to skip, or 'q' to quit: ");
|
||||||
|
|
||||||
|
string input = Console.ReadLine()?.Trim();
|
||||||
|
|
||||||
|
if(input == null)
|
||||||
|
return (null, true);
|
||||||
|
|
||||||
|
if(input.Length == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(input.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (null, true);
|
||||||
|
|
||||||
|
if(input.Equals("s", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (null, false);
|
||||||
|
|
||||||
|
if(input.Equals("n", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Console.Write($" Create new platform named \"{igdbPlatform.Name}\"? [Y/n/q]: ");
|
||||||
|
string confirm = Console.ReadLine()?.Trim();
|
||||||
|
|
||||||
|
if(confirm != null && confirm.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (null, true);
|
||||||
|
|
||||||
|
if(confirm == null || confirm.Length == 0 || confirm.Equals("y", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (new SoftwarePlatform { Name = igdbPlatform.Name }, false);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SoftwarePlatform> candidates = platforms
|
||||||
|
.Where(p => p.Name.Contains(input, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderBy(p => p.Name).ToList();
|
||||||
|
|
||||||
|
if(candidates.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" No local platforms match that text. Try again.");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < candidates.Count; i++)
|
||||||
|
Console.WriteLine($" [{i + 1}] {candidates[i].Name}");
|
||||||
|
|
||||||
|
Console.Write($" Pick a number (1-{candidates.Count}), 'q' to quit, or anything else to search again: ");
|
||||||
|
string choice = Console.ReadLine()?.Trim();
|
||||||
|
|
||||||
|
if(choice == null)
|
||||||
|
return (null, true);
|
||||||
|
|
||||||
|
if(choice.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (null, true);
|
||||||
|
|
||||||
|
if(int.TryParse(choice, out int idx) && idx >= 1 && idx <= candidates.Count)
|
||||||
|
return (candidates[idx - 1], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static List<string> Variants(SoftwarePlatform platform)
|
static List<string> Variants(SoftwarePlatform platform)
|
||||||
{
|
{
|
||||||
var variants = new List<string> { platform.Name };
|
var variants = new List<string> { platform.Name };
|
||||||
|
|||||||
Reference in New Issue
Block a user