feat: Implement AI CSV overwrite confirmation and update CSV output path based on destination

This commit is contained in:
Maddo 2026-05-24 18:45:51 +02:00
commit f3ac1ea920
3 changed files with 214 additions and 0 deletions

View file

@ -22,6 +22,7 @@ using System.Text.RegularExpressions;
using AutoMapper;
using MaddoShared;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace ImageCatalog_2
{
@ -82,6 +83,8 @@ namespace ImageCatalog_2
private sealed record ParsedFaceMatcherRow(string PhotoId, double? Score, string RawRow, string DebugSummary);
private const string AiCsvOverwriteDialogTitle = "File CSV gia esistente";
// ComboBox collections
public List<string> AvailableFonts { get; }
public List<string> VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" };
@ -145,6 +148,12 @@ namespace ImageCatalog_2
private async Task StartAiAsync()
{
if (!await ConfirmAiCsvOverwriteIfNeededAsync().ConfigureAwait(false))
{
await InvokeOnUiThreadAsync(() => NumberAiStatsSummary = "OCR annullato.").ConfigureAwait(false);
return;
}
MainToken = new CancellationTokenSource();
try
{
@ -267,6 +276,8 @@ namespace ImageCatalog_2
set => _ai.CsvOutputPath = value;
}
public Func<string, string, Task<bool>>? ConfirmAiCsvOverwriteAsync { get; set; }
public bool UseNumberAiGpu
{
get => _ai.UseNumberAiGpu;
@ -641,6 +652,11 @@ namespace ImageCatalog_2
return;
}
if (string.Equals(e.PropertyName, nameof(PathSettingsViewModel.DestinationPath), StringComparison.Ordinal))
{
UpdateAiCsvOutputPathForDestination();
}
NotifyPropertyChanged(e.PropertyName);
}
@ -2403,6 +2419,103 @@ namespace ImageCatalog_2
});
}
internal async Task<bool> ConfirmAiCsvOverwriteIfNeededAsync()
{
var csvOutputPath = NormalizeFilePathArgument(CsvOutputPath);
if (string.IsNullOrWhiteSpace(csvOutputPath) || !File.Exists(csvOutputPath))
{
return true;
}
var confirmOverwrite = ConfirmAiCsvOverwriteAsync;
if (confirmOverwrite is null)
{
return true;
}
var message = $"Il file CSV esiste gia:\n{csvOutputPath}\n\nVuoi sovrascriverlo? Se scegli Annulla l'operazione OCR non verra avviata.";
return await confirmOverwrite(AiCsvOverwriteDialogTitle, message).ConfigureAwait(false);
}
internal void UpdateAiCsvOutputPathForDestination()
{
var updatedPath = BuildAiCsvOutputPathForDestination(CsvOutputPath, DestinationPath);
if (string.Equals(updatedPath, CsvOutputPath, StringComparison.Ordinal))
{
return;
}
CsvOutputPath = updatedPath;
}
internal static string BuildAiCsvOutputPathForDestination(string currentCsvOutputPath, string destinationPath)
{
var normalizedCsvPath = NormalizeFilePathArgument(currentCsvOutputPath);
if (string.IsNullOrWhiteSpace(normalizedCsvPath))
{
return currentCsvOutputPath;
}
var directory = Path.GetDirectoryName(normalizedCsvPath);
var destinationFolderName = GetDestinationFolderName(destinationPath);
if (string.IsNullOrWhiteSpace(destinationFolderName))
{
return normalizedCsvPath;
}
var extension = Path.GetExtension(normalizedCsvPath);
if (string.IsNullOrWhiteSpace(extension))
{
extension = ".csv";
}
var safeFileName = SanitizeFileName(destinationFolderName) + extension;
return string.IsNullOrWhiteSpace(directory)
? safeFileName
: Path.Combine(directory, safeFileName);
}
private static string GetDestinationFolderName(string destinationPath)
{
var normalizedDestinationPath = NormalizeDirectoryPathArgument(destinationPath);
if (string.IsNullOrWhiteSpace(normalizedDestinationPath))
{
return string.Empty;
}
var trimmedPath = normalizedDestinationPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (string.IsNullOrWhiteSpace(trimmedPath))
{
return string.Empty;
}
var rootPath = Path.GetPathRoot(trimmedPath)?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (!string.IsNullOrWhiteSpace(rootPath)
&& string.Equals(trimmedPath, rootPath, StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}
return Path.GetFileName(trimmedPath);
}
private static string SanitizeFileName(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var invalidFileNameChars = Path.GetInvalidFileNameChars();
var builder = new StringBuilder(value.Length);
foreach (var character in value)
{
builder.Append(Array.IndexOf(invalidFileNameChars, character) >= 0 ? '_' : character);
}
return builder.ToString();
}
private void SetUseFaceGpu(bool value)
{
var currentValue = _ai.UseFaceGpu;