Agregado exportacion a Excel desde la ventana de Analizar la Matriz. Agregada la correccion de las columnas de descripcion.
This commit is contained in:
parent
3a2e87cd75
commit
8573d942c4
|
@ -59,6 +59,7 @@
|
||||||
<None Remove="Images\base.png" />
|
<None Remove="Images\base.png" />
|
||||||
<None Remove="motor2.png" />
|
<None Remove="motor2.png" />
|
||||||
<None Remove="tank.png" />
|
<None Remove="tank.png" />
|
||||||
|
<None Remove="IA\appsettings.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
||||||
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
||||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
||||||
|
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
@ -142,6 +144,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Update="IA\appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
<None Update="Properties\Settings.settings">
|
<None Update="Properties\Settings.settings">
|
||||||
<Generator>SettingsSingleFileGenerator</Generator>
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
@ -157,4 +162,10 @@
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="IA\appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"ApiKeys": {
|
||||||
|
"OpenAI": "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",
|
||||||
|
"Groq": "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",
|
||||||
|
"Grok": "xai-yR7J4s9VIchdjdsaIMP3zzZJBPa98pySDbavh6hEosG3eII9OtQhoKsNUofKUsKrvNUIW15N11BD5cEa"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,436 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LanguageDetection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace GTPCorrgir
|
||||||
|
{
|
||||||
|
|
||||||
|
public class Opciones
|
||||||
|
{
|
||||||
|
public enum LLM_a_Usar
|
||||||
|
{
|
||||||
|
OpenAI,
|
||||||
|
Ollama,
|
||||||
|
Groq,
|
||||||
|
Grok
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum modoDeUso
|
||||||
|
{
|
||||||
|
Corregir,
|
||||||
|
Ortografia,
|
||||||
|
Traducir_a_Ingles,
|
||||||
|
Traducir_a_Italiano,
|
||||||
|
Traducir_a_Espanol,
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
|
||||||
|
{
|
||||||
|
{ LLM_a_Usar.Ollama, "Ollama" },
|
||||||
|
{ LLM_a_Usar.Groq, "Groq" },
|
||||||
|
{ LLM_a_Usar.Grok, "Grok" },
|
||||||
|
{ LLM_a_Usar.OpenAI, "OpenAI" },
|
||||||
|
};
|
||||||
|
|
||||||
|
public LLM_a_Usar LLM { get; set; }
|
||||||
|
public modoDeUso modo { get; set; }
|
||||||
|
|
||||||
|
public string nombreDeLLM()
|
||||||
|
{
|
||||||
|
return nombreLLM[LLM];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Opciones() { } // Changed from private to public
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ApiSettings
|
||||||
|
{
|
||||||
|
public class ApiKeySection
|
||||||
|
{
|
||||||
|
public string OpenAI { get; set; }
|
||||||
|
public string Groq { get; set; }
|
||||||
|
public string Grok { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiKeySection ApiKeys { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class gtpask : IDisposable
|
||||||
|
{
|
||||||
|
private string _openAiApiKey;
|
||||||
|
private string _groqApiKey;
|
||||||
|
private string _grokApiKey;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private bool _disposed;
|
||||||
|
private readonly LanguageDetector _languageDetector;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, string> _languageMap = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "en", "English" },
|
||||||
|
{ "es", "Spanish" },
|
||||||
|
{ "it", "Italian" },
|
||||||
|
{ "pt", "Portuguese" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public string IdiomaDetectado { get; private set; }
|
||||||
|
public string TextoACorregir { get; set; }
|
||||||
|
public string TextoCorregido { get; private set; }
|
||||||
|
public string TextodeSistema { get; set; }
|
||||||
|
|
||||||
|
public gtpask()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient = new HttpClient();
|
||||||
|
_languageDetector = new LanguageDetector();
|
||||||
|
_languageDetector.AddLanguages("en", "es", "it", "pt");
|
||||||
|
|
||||||
|
LoadApiKeys();
|
||||||
|
InitializeHttpClient();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Failed to initialize gtpask", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadApiKeys()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "IA/appsettings.json");
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Configuration file (appsettings.json) not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string jsonContent = File.ReadAllText(configPath);
|
||||||
|
var settings = JsonConvert.DeserializeObject<ApiSettings>(jsonContent);
|
||||||
|
|
||||||
|
_openAiApiKey = settings?.ApiKeys?.OpenAI;
|
||||||
|
_groqApiKey = settings?.ApiKeys?.Groq;
|
||||||
|
_grokApiKey = settings?.ApiKeys?.Grok;
|
||||||
|
|
||||||
|
ValidateApiKeys();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Failed to load API keys", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateApiKeys()
|
||||||
|
{
|
||||||
|
var missingKeys = new List<string>();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_openAiApiKey)) missingKeys.Add("OpenAI");
|
||||||
|
if (string.IsNullOrEmpty(_groqApiKey)) missingKeys.Add("Groq");
|
||||||
|
if (string.IsNullOrEmpty(_grokApiKey)) missingKeys.Add("Grok");
|
||||||
|
|
||||||
|
if (missingKeys.Any())
|
||||||
|
{
|
||||||
|
string missingKeysStr = string.Join(", ", missingKeys);
|
||||||
|
throw new ApplicationException($"Missing API keys: {missingKeysStr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeHttpClient()
|
||||||
|
{
|
||||||
|
_httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DetectarIdioma()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);
|
||||||
|
IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido");
|
||||||
|
|
||||||
|
return IdiomaDetectado != "Desconocido";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private async Task ProcesarTextoConLLM( Opciones Modelo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string respuestaLLM;
|
||||||
|
|
||||||
|
switch (Modelo.LLM)
|
||||||
|
{
|
||||||
|
case Opciones.LLM_a_Usar.OpenAI:
|
||||||
|
respuestaLLM = await CallOpenAiApi();
|
||||||
|
break;
|
||||||
|
case Opciones.LLM_a_Usar.Ollama:
|
||||||
|
respuestaLLM = await CallOllamaApi();
|
||||||
|
break;
|
||||||
|
case Opciones.LLM_a_Usar.Groq:
|
||||||
|
respuestaLLM = await CallGroqAiApi();
|
||||||
|
break;
|
||||||
|
case Opciones.LLM_a_Usar.Grok:
|
||||||
|
respuestaLLM = await CallGrokApi();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("LLM no válido");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(respuestaLLM))
|
||||||
|
{
|
||||||
|
throw new ApplicationException("No se recibió respuesta del LLM");
|
||||||
|
}
|
||||||
|
|
||||||
|
TextoCorregido = respuestaLLM;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SimularCorreccion()
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
TextoCorregido = "Texto simulado de prueba";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CallGrokApi()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_grokApiKey}");
|
||||||
|
|
||||||
|
var requestData = new
|
||||||
|
{
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "system", content = TextodeSistema },
|
||||||
|
new { role = "user", content = TextoACorregir }
|
||||||
|
},
|
||||||
|
model = "grok-beta",
|
||||||
|
stream = false,
|
||||||
|
temperature = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return await EnviarSolicitudLLM("https://api.x.ai/v1/chat/completions", requestData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CallOllamaApi()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var requestData = new
|
||||||
|
{
|
||||||
|
model = "llama3.2:latest",
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "system", content = TextodeSistema },
|
||||||
|
new { role = "user", content = TextoACorregir }
|
||||||
|
},
|
||||||
|
stream = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return await EnviarSolicitudLLM("http://127.0.0.1:11434/api/chat", requestData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CallOpenAiApi()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_openAiApiKey}");
|
||||||
|
|
||||||
|
var requestData = new
|
||||||
|
{
|
||||||
|
model = "gpt-4o-mini",
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "system", content = TextodeSistema },
|
||||||
|
new { role = "user", content = TextoACorregir }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return await EnviarSolicitudLLM("https://api.openai.com/v1/chat/completions", requestData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CallGroqAiApi()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_groqApiKey}");
|
||||||
|
|
||||||
|
var requestData = new
|
||||||
|
{
|
||||||
|
model = "llama-3.2-3b-preview",
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "system", content = TextodeSistema },
|
||||||
|
new { role = "user", content = TextoACorregir }
|
||||||
|
},
|
||||||
|
max_tokens = 2048,
|
||||||
|
stream = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return await EnviarSolicitudLLM("https://api.groq.com/openai/v1/chat/completions", requestData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> EnviarSolicitudLLM(string endpoint, object requestData)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var content = new StringContent(
|
||||||
|
JsonConvert.SerializeObject(requestData),
|
||||||
|
Encoding.UTF8,
|
||||||
|
"application/json"
|
||||||
|
);
|
||||||
|
|
||||||
|
using var response = await _httpClient.PostAsync(endpoint, content);
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"Error en la solicitud HTTP: {response.StatusCode} - {responseContent}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = JsonConvert.DeserializeObject<dynamic>(responseContent);
|
||||||
|
|
||||||
|
// Manejar diferentes formatos de respuesta según el LLM
|
||||||
|
if (endpoint.Contains("ollama"))
|
||||||
|
{
|
||||||
|
if (data.done == true && data.message != null)
|
||||||
|
{
|
||||||
|
return data.message.content;
|
||||||
|
}
|
||||||
|
throw new ApplicationException("Formato de respuesta de Ollama inválido");
|
||||||
|
}
|
||||||
|
else // OpenAI, Groq, Grok
|
||||||
|
{
|
||||||
|
if (data.choices != null && data.choices.Count > 0)
|
||||||
|
{
|
||||||
|
return data.choices[0].message.content;
|
||||||
|
}
|
||||||
|
throw new ApplicationException("No se encontró contenido en la respuesta del LLM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CorregirTexto()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(TextoACorregir))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("TextoACorregir cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(TextodeSistema))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("TextodeSistema cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
var opciones = new Opciones { LLM = Opciones.LLM_a_Usar.Grok };
|
||||||
|
await ProcesarTextoConLLM(opciones);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new LLMException("Error during text correction", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_httpClient?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~gtpask()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clase auxiliar para manejar excepciones específicas de la aplicación
|
||||||
|
public class LLMException : Exception
|
||||||
|
{
|
||||||
|
public LLMException(string message) : base(message) { }
|
||||||
|
public LLMException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clase auxiliar para validación
|
||||||
|
public static class Validations
|
||||||
|
{
|
||||||
|
public static void ValidateNotNull(object value, string paramName)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(paramName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ValidateNotNullOrEmpty(string value, string paramName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Value cannot be null or empty", paramName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import PyLibrary.funciones_comunes as fc
|
||||||
|
from openai import OpenAI
|
||||||
|
from openai_api_key import openai_api_key
|
||||||
|
|
||||||
|
# Definir el logger a nivel de módulo
|
||||||
|
logger = None
|
||||||
|
|
||||||
|
def corregir_texto_batch(text_pairs, logger):
|
||||||
|
"""
|
||||||
|
Corrige errores de OCR en lotes de pares de texto usando GPT-4.
|
||||||
|
Cada par consiste en dos versiones del mismo texto en diferentes idiomas.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text_pairs: Lista de tuplas (texto1, texto2) donde cada texto es una lectura OCR
|
||||||
|
logger: Logger para registrar el proceso
|
||||||
|
"""
|
||||||
|
client = OpenAI(api_key=openai_api_key())
|
||||||
|
|
||||||
|
system_prompt = """You are an OCR correction specialist. For each pair of texts, which are OCR readings of the same text in different languages, analyze and correct any obvious OCR errors.
|
||||||
|
Pay special attention to:
|
||||||
|
- Incorrectly joined words (missing spaces)
|
||||||
|
- Wrong character recognition (0 vs O, 1 vs I, etc.)
|
||||||
|
- Extra or missing characters
|
||||||
|
Return the corrected versions maintaining the general structure and meaning.
|
||||||
|
|
||||||
|
Input format: List of text pairs
|
||||||
|
Expected output format: List of corrected pairs in the same order
|
||||||
|
Example input: [["PULSANTE DI STARTNASTRI", "START PUSHBUTTONTTOP"]]
|
||||||
|
Example output: [["PULSANTE DI START NASTRI", "START PUSH BUTTON TOP"]]"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Convertir los pares de texto a formato JSON
|
||||||
|
request_payload = json.dumps({"pairs": text_pairs})
|
||||||
|
logger.info(f"Solicitando corrección para el lote de textos:\n{request_payload}")
|
||||||
|
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model="gpt-4",
|
||||||
|
messages=[
|
||||||
|
{"role": "system", "content": system_prompt},
|
||||||
|
{"role": "user", "content": request_payload},
|
||||||
|
],
|
||||||
|
max_tokens=2000,
|
||||||
|
temperature=0.3,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Procesar la respuesta
|
||||||
|
corrections = json.loads(response.choices[0].message.content)
|
||||||
|
logger.info(f"Correcciones recibidas:\n{corrections}")
|
||||||
|
return corrections["pairs"]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error en la corrección por lotes: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def procesar_archivo(config, archivo_entrada):
|
||||||
|
"""
|
||||||
|
Procesa el archivo Excel con los textos OCR y genera un nuevo archivo con las correcciones.
|
||||||
|
"""
|
||||||
|
logger.info(f"Iniciando procesamiento de {archivo_entrada}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Leer el archivo de entrada
|
||||||
|
df = fc.read_dataframe_with_cleanup_retries(archivo_entrada)
|
||||||
|
|
||||||
|
if config.columna_origen1 not in df.columns or config.columna_origen2 not in df.columns:
|
||||||
|
logger.error(f"Columnas requeridas no encontradas en el archivo")
|
||||||
|
print(f"Error: Las columnas {config.columna_origen1} y/o {config.columna_origen2} no existen en el archivo")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Filtrar filas donde ambas columnas tienen contenido
|
||||||
|
df_filtered = df.dropna(subset=[config.columna_origen1, config.columna_origen2])
|
||||||
|
df_filtered = df_filtered[
|
||||||
|
(df_filtered[config.columna_origen1].astype(str).str.strip() != '') &
|
||||||
|
(df_filtered[config.columna_origen2].astype(str).str.strip() != '')
|
||||||
|
]
|
||||||
|
|
||||||
|
# Crear columnas para textos corregidos
|
||||||
|
df[f"{config.columna_origen1}_Corregido"] = df[config.columna_origen1]
|
||||||
|
df[f"{config.columna_origen2}_Corregido"] = df[config.columna_origen2]
|
||||||
|
|
||||||
|
# Procesar en lotes de 10 pares
|
||||||
|
batch_size = 10
|
||||||
|
total_rows = len(df_filtered)
|
||||||
|
|
||||||
|
progress_bar = fc.ProgressBar(
|
||||||
|
total_rows, prefix="Procesando textos:", suffix="Completado"
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(0, total_rows, batch_size):
|
||||||
|
batch_df = df_filtered.iloc[i:i+batch_size]
|
||||||
|
|
||||||
|
# Preparar pares de textos para el lote
|
||||||
|
text_pairs = [
|
||||||
|
[str(row[config.columna_origen1]).strip(), str(row[config.columna_origen2]).strip()]
|
||||||
|
for _, row in batch_df.iterrows()
|
||||||
|
]
|
||||||
|
|
||||||
|
# Obtener correcciones para el lote
|
||||||
|
corrections = corregir_texto_batch(text_pairs, logger)
|
||||||
|
|
||||||
|
if corrections:
|
||||||
|
# Aplicar correcciones al DataFrame original
|
||||||
|
for j, correction in enumerate(corrections):
|
||||||
|
idx = batch_df.index[j]
|
||||||
|
df.at[idx, f"{config.columna_origen1}_Corregido"] = correction[0]
|
||||||
|
df.at[idx, f"{config.columna_origen2}_Corregido"] = correction[1]
|
||||||
|
|
||||||
|
progress_bar.update(min(i + batch_size, total_rows))
|
||||||
|
|
||||||
|
progress_bar.finish()
|
||||||
|
|
||||||
|
# Guardar resultados
|
||||||
|
output_path = config.get_output_path()
|
||||||
|
fc.save_dataframe_with_retries(df, output_path)
|
||||||
|
|
||||||
|
logger.info(f"Archivo procesado y guardado en: {output_path}")
|
||||||
|
print(f"\nArchivo procesado y guardado en: {output_path}")
|
||||||
|
print(f"Se procesaron {total_rows} pares de texto.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error durante el procesamiento: {str(e)}")
|
||||||
|
print(f"Error durante el procesamiento: {str(e)}")
|
||||||
|
|
||||||
|
def run(config):
|
||||||
|
global logger
|
||||||
|
logger = fc.configurar_logger(config.work_dir)
|
||||||
|
script_name = os.path.basename(__file__)
|
||||||
|
print(f"\rIniciando: {script_name}\r")
|
||||||
|
|
||||||
|
# Solicitar archivo de entrada
|
||||||
|
from tkinter import filedialog
|
||||||
|
archivo_entrada = filedialog.askopenfilename(
|
||||||
|
title="Seleccione el archivo con textos OCR",
|
||||||
|
filetypes=[("Excel files", "*.xls*")]
|
||||||
|
)
|
||||||
|
|
||||||
|
if archivo_entrada:
|
||||||
|
procesar_archivo(config, archivo_entrada)
|
||||||
|
else:
|
||||||
|
print("No se seleccionó ningún archivo para procesar.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import menu_ocr_correction
|
||||||
|
menu_ocr_correction.main()
|
|
@ -80,7 +80,7 @@ namespace CtrEditor
|
||||||
public ICommand TBMultiPageExtractTagsCommand { get; }
|
public ICommand TBMultiPageExtractTagsCommand { get; }
|
||||||
public ICommand TBMultiPageAnalizeCommand { get; }
|
public ICommand TBMultiPageAnalizeCommand { get; }
|
||||||
public ICommand TBAnalyzeMatrixCommand { get; }
|
public ICommand TBAnalyzeMatrixCommand { get; }
|
||||||
|
public ICommand TBMultiPageMatrixCommand { get; }
|
||||||
|
|
||||||
// Evento que se dispara cuando se selecciona una nueva imagen
|
// Evento que se dispara cuando se selecciona una nueva imagen
|
||||||
public event EventHandler<string> ImageSelected;
|
public event EventHandler<string> ImageSelected;
|
||||||
|
@ -112,6 +112,8 @@ namespace CtrEditor
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<string> recentDirectories;
|
private ObservableCollection<string> recentDirectories;
|
||||||
|
|
||||||
|
private bool inhibitSaveChangesControl;
|
||||||
|
|
||||||
public ICommand OpenRecentDirectoryCommand { get; private set; }
|
public ICommand OpenRecentDirectoryCommand { get; private set; }
|
||||||
|
|
||||||
partial void OnIsSimulationRunningChanged(bool value)
|
partial void OnIsSimulationRunningChanged(bool value)
|
||||||
|
@ -204,7 +206,7 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
if (HasUnsavedChanges)
|
if (HasUnsavedChanges && !inhibitSaveChangesControl)
|
||||||
{
|
{
|
||||||
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
||||||
"Save Changes",
|
"Save Changes",
|
||||||
|
@ -257,7 +259,12 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
VistaFiltrada = CollectionViewSource.GetDefaultView(ObjetosSimulables);
|
VistaFiltrada = CollectionViewSource.GetDefaultView(ObjetosSimulables);
|
||||||
VistaFiltrada.Filter = FiltrarObjetos;
|
VistaFiltrada.Filter = FiltrarObjetos;
|
||||||
ObjetosSimulables.CollectionChanged += (s, e) => VistaFiltrada.Refresh();
|
ObjetosSimulables.CollectionChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
VistaFiltrada.Refresh();
|
||||||
|
if (!inhibitSaveChangesControl && e.Action != NotifyCollectionChangedAction.Move)
|
||||||
|
HasUnsavedChanges = true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -314,6 +321,7 @@ namespace CtrEditor
|
||||||
TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
|
TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
|
||||||
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
|
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
|
||||||
TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand);
|
TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand);
|
||||||
|
TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);
|
||||||
|
|
||||||
stopwatch_Sim = new Stopwatch();
|
stopwatch_Sim = new Stopwatch();
|
||||||
stopwatch_Sim.Start();
|
stopwatch_Sim.Start();
|
||||||
|
@ -530,7 +538,7 @@ namespace CtrEditor
|
||||||
SaveStateObjetosSimulables();
|
SaveStateObjetosSimulables();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WaitForUIUpdateAsync()
|
public async Task WaitForUIUpdateAsync()
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
|
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
|
||||||
|
@ -539,6 +547,18 @@ namespace CtrEditor
|
||||||
|
|
||||||
private async void MultiPageExtractTagsCommand()
|
private async void MultiPageExtractTagsCommand()
|
||||||
{
|
{
|
||||||
|
if (HasUnsavedChanges)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
||||||
|
"Save Changes",
|
||||||
|
MessageBoxButton.YesNoCancel);
|
||||||
|
|
||||||
|
if (result == MessageBoxResult.Cancel)
|
||||||
|
return;
|
||||||
|
else if (result == MessageBoxResult.Yes)
|
||||||
|
SaveStateObjetosSimulables();
|
||||||
|
}
|
||||||
|
|
||||||
var ImagenesSeleccionadas = new ObservableCollection<string>
|
var ImagenesSeleccionadas = new ObservableCollection<string>
|
||||||
{
|
{
|
||||||
SelectedImage
|
SelectedImage
|
||||||
|
@ -552,6 +572,9 @@ namespace CtrEditor
|
||||||
selectPagesWindow.DataContext = selectPagesViewModel;
|
selectPagesWindow.DataContext = selectPagesViewModel;
|
||||||
selectPagesWindow.ShowDialog();
|
selectPagesWindow.ShowDialog();
|
||||||
|
|
||||||
|
inhibitSaveChangesControl = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
|
if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
|
||||||
foreach (var page in ImagenesSeleccionadas)
|
foreach (var page in ImagenesSeleccionadas)
|
||||||
{
|
{
|
||||||
|
@ -560,6 +583,11 @@ namespace CtrEditor
|
||||||
ExtraerTags();
|
ExtraerTags();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
inhibitSaveChangesControl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void MultiPageAnalizeCommand()
|
private async void MultiPageAnalizeCommand()
|
||||||
{
|
{
|
||||||
|
@ -576,6 +604,13 @@ namespace CtrEditor
|
||||||
selectPagesWindow.DataContext = selectPagesViewModel;
|
selectPagesWindow.DataContext = selectPagesViewModel;
|
||||||
selectPagesWindow.ShowDialog();
|
selectPagesWindow.ShowDialog();
|
||||||
|
|
||||||
|
bool originalHasUnsavedChanges = HasUnsavedChanges;
|
||||||
|
HasUnsavedChanges = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
|
||||||
|
{
|
||||||
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
|
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
|
||||||
|
|
||||||
foreach (var page in ImagenesSeleccionadas)
|
foreach (var page in ImagenesSeleccionadas)
|
||||||
|
@ -587,6 +622,12 @@ namespace CtrEditor
|
||||||
|
|
||||||
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
|
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
HasUnsavedChanges = originalHasUnsavedChanges;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,6 +1150,51 @@ namespace CtrEditor
|
||||||
matrixPreviewWindow.DataContext = matrixPreviewViewModel;
|
matrixPreviewWindow.DataContext = matrixPreviewViewModel;
|
||||||
matrixPreviewWindow.ShowDialog();
|
matrixPreviewWindow.ShowDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void MultiPageMatrixCommand()
|
||||||
|
{
|
||||||
|
if (HasUnsavedChanges)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
||||||
|
"Save Changes",
|
||||||
|
MessageBoxButton.YesNoCancel);
|
||||||
|
|
||||||
|
if (result == MessageBoxResult.Cancel)
|
||||||
|
return;
|
||||||
|
else if (result == MessageBoxResult.Yes)
|
||||||
|
SaveStateObjetosSimulables();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ImagenesSeleccionadas = new ObservableCollection<string>
|
||||||
|
{
|
||||||
|
SelectedImage
|
||||||
|
};
|
||||||
|
|
||||||
|
StopSimulation();
|
||||||
|
|
||||||
|
var selectPagesWindow = new SelectPages();
|
||||||
|
var selectPagesViewModel = new SelectPagesViewModel();
|
||||||
|
selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas);
|
||||||
|
selectPagesWindow.DataContext = selectPagesViewModel;
|
||||||
|
selectPagesWindow.ShowDialog();
|
||||||
|
|
||||||
|
inhibitSaveChangesControl = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK)
|
||||||
|
{
|
||||||
|
var matrixPreviewWindow = new MatrixPreviewWindow();
|
||||||
|
var matrixPreviewViewModel = new MatrixPreviewViewModel();
|
||||||
|
matrixPreviewViewModel.Initialize(this, matrixPreviewWindow, ImagenesSeleccionadas);
|
||||||
|
matrixPreviewWindow.DataContext = matrixPreviewViewModel;
|
||||||
|
matrixPreviewWindow.ShowDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
inhibitSaveChangesControl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class SimulationData
|
public class SimulationData
|
||||||
{
|
{
|
||||||
|
|
|
@ -135,12 +135,6 @@
|
||||||
<TextBlock Text="Extraer Tags" />
|
<TextBlock Text="Extraer Tags" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Command="{Binding TBMultiPageExtractTagsCommand}" ToolTip="Extraer Tags in multiple pages.">
|
|
||||||
<StackPanel>
|
|
||||||
<Image Source="Icons/extract.png" Width="24" Height="24" />
|
|
||||||
<TextBlock Text="Multi Page Extract" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
<Button Command="{Binding TBMultiPageAnalizeCommand}"
|
<Button Command="{Binding TBMultiPageAnalizeCommand}"
|
||||||
ToolTip="Analyze Tags in multiple pages.">
|
ToolTip="Analyze Tags in multiple pages.">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
@ -154,6 +148,12 @@
|
||||||
<TextBlock Text="Analyze Matrix" />
|
<TextBlock Text="Analyze Matrix" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button Command="{Binding TBMultiPageMatrixCommand}" ToolTip="Analyze Matrix (Multiple Pages)">
|
||||||
|
<StackPanel>
|
||||||
|
<Image Source="Icons/analyze.png" Width="24" Height="24" />
|
||||||
|
<TextBlock Text="Multi Page Matrix" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</ToolBarTray>
|
</ToolBarTray>
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
<Window x:Class="CtrEditor.PopUps.ColumnSelectionDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Selección de Columnas e Idiomas"
|
||||||
|
Height="400"
|
||||||
|
Width="500"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
ShowInTaskbar="False">
|
||||||
|
<Grid Margin="20">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Label x:Name="SourceColumnHeaderLabel"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="Columna origen:"
|
||||||
|
Margin="0,0,10,5"/>
|
||||||
|
|
||||||
|
<ComboBox Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
x:Name="SourceColumnComboBox"
|
||||||
|
Margin="0,0,10,15"
|
||||||
|
Height="30"
|
||||||
|
SelectionChanged="ColumnComboBox_SelectionChanged"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="Idioma origen:"
|
||||||
|
Margin="0,0,10,5"/>
|
||||||
|
|
||||||
|
<ComboBox Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
x:Name="SourceLanguageComboBox"
|
||||||
|
Margin="0,0,10,15"
|
||||||
|
Height="30"/>
|
||||||
|
|
||||||
|
<Label x:Name="TargetColumnHeaderLabel"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Columna destino:"
|
||||||
|
Margin="0,0,0,5"/>
|
||||||
|
|
||||||
|
<ComboBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
x:Name="TargetColumnComboBox"
|
||||||
|
Margin="0,0,0,15"
|
||||||
|
Height="30"
|
||||||
|
SelectionChanged="ColumnComboBox_SelectionChanged"/>
|
||||||
|
|
||||||
|
<Label Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Idioma destino:"
|
||||||
|
Margin="0,0,0,5"/>
|
||||||
|
|
||||||
|
<ComboBox Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
x:Name="TargetLanguageComboBox"
|
||||||
|
Margin="0,0,0,15"
|
||||||
|
Height="30"/>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="7"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Button Content="Aceptar"
|
||||||
|
Click="OkButton_Click"
|
||||||
|
Width="90"
|
||||||
|
Height="30"
|
||||||
|
Margin="0,0,10,0"/>
|
||||||
|
|
||||||
|
<Button Content="Cancelar"
|
||||||
|
Click="CancelButton_Click"
|
||||||
|
Width="90"
|
||||||
|
Height="30"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
|
@ -0,0 +1,197 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace CtrEditor.PopUps
|
||||||
|
{
|
||||||
|
public partial class ColumnSelectionDialog : Window
|
||||||
|
{
|
||||||
|
private readonly List<string> _columnNames;
|
||||||
|
public string SelectedSourceColumn { get; private set; }
|
||||||
|
public string SelectedTargetColumn { get; private set; }
|
||||||
|
public string SelectedSourceLanguage { get; private set; }
|
||||||
|
public string SelectedTargetLanguage { get; private set; }
|
||||||
|
|
||||||
|
private static readonly List<string> SupportedLanguages = new List<string>
|
||||||
|
{
|
||||||
|
"English",
|
||||||
|
"Spanish",
|
||||||
|
"Italian",
|
||||||
|
"French",
|
||||||
|
"German",
|
||||||
|
"Portuguese"
|
||||||
|
};
|
||||||
|
|
||||||
|
private string _sourceColumnLabel = "Idioma 1";
|
||||||
|
private string _targetColumnLabel = "Idioma 2";
|
||||||
|
|
||||||
|
public string SourceColumnLabel
|
||||||
|
{
|
||||||
|
get => _sourceColumnLabel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sourceColumnLabel = value;
|
||||||
|
if (SourceColumnHeaderLabel != null)
|
||||||
|
SourceColumnHeaderLabel.Content = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TargetColumnLabel
|
||||||
|
{
|
||||||
|
get => _targetColumnLabel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_targetColumnLabel = value;
|
||||||
|
if (TargetColumnHeaderLabel != null)
|
||||||
|
TargetColumnHeaderLabel.Content = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnSelectionDialog(List<string> columnNames)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_columnNames = columnNames;
|
||||||
|
|
||||||
|
SourceColumnComboBox.ItemsSource = columnNames;
|
||||||
|
TargetColumnComboBox.ItemsSource = columnNames;
|
||||||
|
SourceLanguageComboBox.ItemsSource = SupportedLanguages;
|
||||||
|
TargetLanguageComboBox.ItemsSource = SupportedLanguages;
|
||||||
|
|
||||||
|
// Pre-select languages from saved settings
|
||||||
|
PreSelectLanguages();
|
||||||
|
|
||||||
|
// Set default labels
|
||||||
|
SourceColumnHeaderLabel.Content = SourceColumnLabel;
|
||||||
|
TargetColumnHeaderLabel.Content = TargetColumnLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PreSelectLanguages()
|
||||||
|
{
|
||||||
|
// First try to load last used column pair
|
||||||
|
var lastPair = EstadoPersistente.Instance.ColumnPairs.LastOrDefault();
|
||||||
|
if (lastPair != null &&
|
||||||
|
_columnNames.Contains(lastPair.SourceColumn) &&
|
||||||
|
_columnNames.Contains(lastPair.TargetColumn))
|
||||||
|
{
|
||||||
|
SourceColumnComboBox.SelectedItem = lastPair.SourceColumn;
|
||||||
|
TargetColumnComboBox.SelectedItem = lastPair.TargetColumn;
|
||||||
|
}
|
||||||
|
// If no previous pair exists or columns not found, try Descrip columns
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var descColumns = _columnNames.Where(c => c.Contains("Descrip")).ToList();
|
||||||
|
if (descColumns.Any())
|
||||||
|
{
|
||||||
|
SourceColumnComboBox.SelectedItem = descColumns.FirstOrDefault();
|
||||||
|
if (descColumns.Count > 1)
|
||||||
|
{
|
||||||
|
TargetColumnComboBox.SelectedItem = descColumns[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load language mappings
|
||||||
|
if (SourceColumnComboBox.SelectedItem != null)
|
||||||
|
{
|
||||||
|
var sourceMapping = EstadoPersistente.Instance.ColumnLanguages
|
||||||
|
.FirstOrDefault(m => m.ColumnName == SourceColumnComboBox.SelectedItem.ToString());
|
||||||
|
if (sourceMapping != null)
|
||||||
|
{
|
||||||
|
SourceLanguageComboBox.SelectedItem = sourceMapping.Language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TargetColumnComboBox.SelectedItem != null)
|
||||||
|
{
|
||||||
|
var targetMapping = EstadoPersistente.Instance.ColumnLanguages
|
||||||
|
.FirstOrDefault(m => m.ColumnName == TargetColumnComboBox.SelectedItem.ToString());
|
||||||
|
if (targetMapping != null)
|
||||||
|
{
|
||||||
|
TargetLanguageComboBox.SelectedItem = targetMapping.Language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ColumnComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var comboBox = sender as System.Windows.Controls.ComboBox;
|
||||||
|
if (comboBox?.SelectedItem == null) return;
|
||||||
|
|
||||||
|
var mapping = EstadoPersistente.Instance.ColumnLanguages
|
||||||
|
.FirstOrDefault(m => m.ColumnName == comboBox.SelectedItem.ToString());
|
||||||
|
|
||||||
|
if (mapping != null)
|
||||||
|
{
|
||||||
|
if (comboBox == SourceColumnComboBox)
|
||||||
|
SourceLanguageComboBox.SelectedItem = mapping.Language;
|
||||||
|
else if (comboBox == TargetColumnComboBox)
|
||||||
|
TargetLanguageComboBox.SelectedItem = mapping.Language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (SourceColumnComboBox.SelectedItem == null ||
|
||||||
|
TargetColumnComboBox.SelectedItem == null ||
|
||||||
|
SourceLanguageComboBox.SelectedItem == null ||
|
||||||
|
TargetLanguageComboBox.SelectedItem == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select both columns and their languages",
|
||||||
|
"Selection Required",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedSourceColumn = SourceColumnComboBox.SelectedItem.ToString();
|
||||||
|
SelectedTargetColumn = TargetColumnComboBox.SelectedItem.ToString();
|
||||||
|
SelectedSourceLanguage = SourceLanguageComboBox.SelectedItem.ToString();
|
||||||
|
SelectedTargetLanguage = TargetLanguageComboBox.SelectedItem.ToString();
|
||||||
|
|
||||||
|
// Save language mappings
|
||||||
|
SaveLanguageMappings();
|
||||||
|
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveLanguageMappings()
|
||||||
|
{
|
||||||
|
// Save language mappings
|
||||||
|
var mappings = EstadoPersistente.Instance.ColumnLanguages;
|
||||||
|
UpdateMapping(mappings, SelectedSourceColumn, SelectedSourceLanguage);
|
||||||
|
UpdateMapping(mappings, SelectedTargetColumn, SelectedTargetLanguage);
|
||||||
|
|
||||||
|
// Save column pair
|
||||||
|
var columnPairs = EstadoPersistente.Instance.ColumnPairs;
|
||||||
|
var lastPair = columnPairs.LastOrDefault();
|
||||||
|
if (lastPair == null ||
|
||||||
|
lastPair.SourceColumn != SelectedSourceColumn ||
|
||||||
|
lastPair.TargetColumn != SelectedTargetColumn)
|
||||||
|
{
|
||||||
|
columnPairs.Add(new ColumnPairMapping
|
||||||
|
{
|
||||||
|
SourceColumn = SelectedSourceColumn,
|
||||||
|
TargetColumn = SelectedTargetColumn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EstadoPersistente.Instance.GuardarEstado();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMapping(List<ColumnLanguageMapping> mappings, string columnName, string language)
|
||||||
|
{
|
||||||
|
var existing = mappings.FirstOrDefault(m => m.ColumnName == columnName);
|
||||||
|
if (existing != null)
|
||||||
|
existing.Language = language;
|
||||||
|
else
|
||||||
|
mappings.Add(new ColumnLanguageMapping { ColumnName = columnName, Language = language });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,9 @@
|
||||||
Background="White"
|
Background="White"
|
||||||
GridLinesVisibility="All"
|
GridLinesVisibility="All"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
ColumnReordered="DataGrid_ColumnReordered">
|
ColumnReordered="DataGrid_ColumnReordered"
|
||||||
|
SelectionMode="Extended"
|
||||||
|
SelectionUnit="Cell">
|
||||||
<DataGrid.Resources>
|
<DataGrid.Resources>
|
||||||
<Style TargetType="DataGridColumnHeader">
|
<Style TargetType="DataGridColumnHeader">
|
||||||
<Setter Property="Background" Value="LightGray"/>
|
<Setter Property="Background" Value="LightGray"/>
|
||||||
|
@ -68,6 +70,14 @@
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||||
|
<Button Content="AI Correction"
|
||||||
|
Command="{Binding AICorrectMatrixCommand}"
|
||||||
|
Margin="5"
|
||||||
|
Padding="10,5"/>
|
||||||
|
<Button Content="Export to Excel"
|
||||||
|
Command="{Binding ExportToExcelCommand}"
|
||||||
|
Margin="5"
|
||||||
|
Padding="10,5"/>
|
||||||
<Button Content="Regenerate Matrix"
|
<Button Content="Regenerate Matrix"
|
||||||
Command="{Binding RegenerateMatrixCommand}"
|
Command="{Binding RegenerateMatrixCommand}"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
using GTPCorrgir;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CtrEditor.Services
|
||||||
|
{
|
||||||
|
public class LLMService
|
||||||
|
{
|
||||||
|
private readonly gtpask _llmProcessor;
|
||||||
|
|
||||||
|
public LLMService()
|
||||||
|
{
|
||||||
|
_llmProcessor = new gtpask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> ProcessText(string text, bool useMarkdown = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_llmProcessor.TextoACorregir = text;
|
||||||
|
// Create system prompt through constructor or initialization
|
||||||
|
_llmProcessor.TextodeSistema = "You are an OCR correction specialist. Analyze and correct any obvious OCR errors." +
|
||||||
|
"\nPay special attention to:" +
|
||||||
|
"\n- Incorrectly joined words (missing spaces)" +
|
||||||
|
"\n- Wrong character recognition (0 vs O, 1 vs I, etc.)" +
|
||||||
|
"\n- Extra or missing characters" +
|
||||||
|
"\nReturn only the corrected text without explanations.";
|
||||||
|
|
||||||
|
// Initialize a new instance with the system prompt
|
||||||
|
await _llmProcessor.CorregirTexto();
|
||||||
|
return _llmProcessor.TextoCorregido;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Error processing text with LLM: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<(string Source, string Target)>> ProcessTextBatch(
|
||||||
|
List<(string Source, string Target)> textPairs,
|
||||||
|
string sourceLanguage = "Unknown",
|
||||||
|
string targetLanguage = "Unknown")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var textPairsJson = JsonConvert.SerializeObject(
|
||||||
|
textPairs.Select(p => new[] { p.Source, p.Target }).ToList()
|
||||||
|
);
|
||||||
|
|
||||||
|
_llmProcessor.TextoACorregir = textPairsJson;
|
||||||
|
_llmProcessor.TextodeSistema = $@"You are an OCR correction specialist working with {sourceLanguage} and {targetLanguage} texts.
|
||||||
|
For each pair, the first text is in {sourceLanguage} and the second text is in {targetLanguage}.
|
||||||
|
|
||||||
|
Pay special attention to:
|
||||||
|
- Language-specific characters and accents for both {sourceLanguage} and {targetLanguage}
|
||||||
|
- Incorrectly joined words (missing spaces)
|
||||||
|
- Wrong character recognition (0 vs O, 1 vs I, etc.)
|
||||||
|
- Extra or missing characters
|
||||||
|
|
||||||
|
Return the corrected versions in JSON format as a list of pairs.
|
||||||
|
Input: [[""source text"", ""target text""]]
|
||||||
|
Expected output format: ```json[[""corrected source"", ""corrected target""]]```";
|
||||||
|
|
||||||
|
await _llmProcessor.CorregirTexto();
|
||||||
|
|
||||||
|
// Extract JSON content from markdown
|
||||||
|
string jsonContent = ExtractJsonFromMarkdown(_llmProcessor.TextoCorregido);
|
||||||
|
if (string.IsNullOrEmpty(jsonContent))
|
||||||
|
{
|
||||||
|
throw new Exception("Could not extract JSON content from LLM response");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = JsonConvert.DeserializeObject<List<string[]>>(jsonContent);
|
||||||
|
return result.Select(pair => (pair[0].TrimEnd('\n'), pair[1].TrimEnd('\n'))).ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Error processing text batch with LLM: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExtractJsonFromMarkdown(string markdownText)
|
||||||
|
{
|
||||||
|
const string jsonStart = "```json";
|
||||||
|
const string codeBlockEnd = "```";
|
||||||
|
|
||||||
|
var startIndex = markdownText.IndexOf(jsonStart);
|
||||||
|
if (startIndex == -1) return null;
|
||||||
|
|
||||||
|
startIndex += jsonStart.Length;
|
||||||
|
var endIndex = markdownText.IndexOf(codeBlockEnd, startIndex);
|
||||||
|
if (endIndex == -1) return null;
|
||||||
|
|
||||||
|
return markdownText.Substring(startIndex, endIndex - startIndex).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_llmProcessor?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,10 @@ using System.Collections.ObjectModel;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Threading; // Add this line
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using ClosedXML.Excel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace CtrEditor.PopUps
|
namespace CtrEditor.PopUps
|
||||||
{
|
{
|
||||||
|
@ -28,7 +31,9 @@ namespace CtrEditor.PopUps
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public bool IsCloned { get; set; }
|
public bool IsCloned { get; set; }
|
||||||
public osBase SourceObject { get; set; }
|
public UniqueId Id { get; set; }
|
||||||
|
public string Source_Image { get; set; }
|
||||||
|
public int Copy_Number { get; set; } // Add this property
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class MatrixPreviewViewModel : ObservableObject
|
public partial class MatrixPreviewViewModel : ObservableObject
|
||||||
|
@ -36,6 +41,12 @@ namespace CtrEditor.PopUps
|
||||||
private Window _window;
|
private Window _window;
|
||||||
private MainViewModel _mainViewModel;
|
private MainViewModel _mainViewModel;
|
||||||
private DataGrid _dataGrid;
|
private DataGrid _dataGrid;
|
||||||
|
private readonly Services.LLMService _llmService;
|
||||||
|
|
||||||
|
public MatrixPreviewViewModel()
|
||||||
|
{
|
||||||
|
_llmService = new Services.LLMService();
|
||||||
|
}
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<MatrixItem> matrixItems;
|
private ObservableCollection<MatrixItem> matrixItems;
|
||||||
|
@ -49,21 +60,24 @@ namespace CtrEditor.PopUps
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private double detailsHeightValue = 150;
|
private double detailsHeightValue = 150;
|
||||||
|
|
||||||
|
private ObservableCollection<string> selectedImages;
|
||||||
|
|
||||||
partial void OnDetailsHeightValueChanged(double value)
|
partial void OnDetailsHeightValueChanged(double value)
|
||||||
{
|
{
|
||||||
DetailsHeight = new GridLength(value);
|
DetailsHeight = new GridLength(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(MainViewModel mainViewModel, Window window)
|
public void Initialize(MainViewModel mainViewModel, Window window, ObservableCollection<string> images = null)
|
||||||
{
|
{
|
||||||
_mainViewModel = mainViewModel;
|
_mainViewModel = mainViewModel;
|
||||||
_window = window;
|
_window = window;
|
||||||
_dataGrid = (_window as MatrixPreviewWindow)?.MatrixPreview;
|
_dataGrid = (_window as MatrixPreviewWindow)?.MatrixPreview;
|
||||||
|
selectedImages = images ?? new ObservableCollection<string> { mainViewModel.SelectedImage };
|
||||||
|
|
||||||
MatrixItems = new ObservableCollection<MatrixItem>();
|
MatrixItems = new ObservableCollection<MatrixItem>();
|
||||||
MatrixRows = new ObservableCollection<Dictionary<string, string>>();
|
MatrixRows = new ObservableCollection<Dictionary<string, string>>();
|
||||||
|
|
||||||
AnalyzeMatrix();
|
AnalyzeMatrixMultiPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateMatrixPreview()
|
private void UpdateMatrixPreview()
|
||||||
|
@ -76,6 +90,9 @@ namespace CtrEditor.PopUps
|
||||||
|
|
||||||
if (!MatrixItems.Any()) return;
|
if (!MatrixItems.Any()) return;
|
||||||
|
|
||||||
|
// Group items by source image and then by column number
|
||||||
|
var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image);
|
||||||
|
|
||||||
// Ordenar items por número de columna
|
// Ordenar items por número de columna
|
||||||
var orderedItems = MatrixItems
|
var orderedItems = MatrixItems
|
||||||
.GroupBy(x => x.ColumnNumber)
|
.GroupBy(x => x.ColumnNumber)
|
||||||
|
@ -95,21 +112,27 @@ namespace CtrEditor.PopUps
|
||||||
_dataGrid.Columns.Add(column);
|
_dataGrid.Columns.Add(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular número de filas necesarias
|
// Process each image's data
|
||||||
|
foreach (var imageGroup in itemsByImage)
|
||||||
|
{
|
||||||
|
// Calculate max rows needed for this image
|
||||||
int maxRows = 1;
|
int maxRows = 1;
|
||||||
var clonedItems = MatrixItems.Where(x => x.IsCloned).ToList();
|
var clonedItems = imageGroup.Where(x => x.IsCloned).ToList();
|
||||||
if (clonedItems.Any())
|
if (clonedItems.Any())
|
||||||
{
|
{
|
||||||
maxRows = clonedItems.Max(x => x.SourceObject is osExtraccionTag tag ? tag.Copy_Number : 0) + 1;
|
maxRows = clonedItems.Max(x => x.Copy_Number) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear filas con valores
|
// Create rows for this image
|
||||||
for (int row = 0; row < maxRows; row++)
|
for (int row = 0; row < maxRows; row++)
|
||||||
{
|
{
|
||||||
var rowData = new Dictionary<string, string>();
|
var rowData = new Dictionary<string, string>();
|
||||||
|
|
||||||
// Agregar valores fijos (no clonados) en todas las filas
|
// Add image identifier
|
||||||
foreach (var item in MatrixItems.Where(x => !x.IsCloned))
|
rowData["Image"] = imageGroup.Key;
|
||||||
|
|
||||||
|
// Add fixed (non-cloned) values in all rows
|
||||||
|
foreach (var item in imageGroup.Where(x => !x.IsCloned))
|
||||||
{
|
{
|
||||||
if (item.ColumnNumber > 0)
|
if (item.ColumnNumber > 0)
|
||||||
{
|
{
|
||||||
|
@ -117,10 +140,10 @@ namespace CtrEditor.PopUps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agregar valores clonados solo en su fila correspondiente
|
// Add cloned values only in their corresponding row
|
||||||
foreach (var item in MatrixItems.Where(x => x.IsCloned))
|
foreach (var item in imageGroup.Where(x => x.IsCloned))
|
||||||
{
|
{
|
||||||
if (item.ColumnNumber > 0 && item.SourceObject is osExtraccionTag tag && tag.Copy_Number == row)
|
if (item.ColumnNumber > 0 && item.Copy_Number == row)
|
||||||
{
|
{
|
||||||
rowData[item.ColumnNumber.ToString()] = item.Value ?? string.Empty;
|
rowData[item.ColumnNumber.ToString()] = item.Value ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
@ -129,6 +152,7 @@ namespace CtrEditor.PopUps
|
||||||
MatrixRows.Add(rowData);
|
MatrixRows.Add(rowData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateColumnHeaders()
|
private void UpdateColumnHeaders()
|
||||||
{
|
{
|
||||||
|
@ -147,8 +171,52 @@ namespace CtrEditor.PopUps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AnalyzeMatrix()
|
private async void AnalyzeMatrixMultiPage()
|
||||||
{
|
{
|
||||||
|
MatrixItems.Clear();
|
||||||
|
bool originalHasUnsavedChanges = _mainViewModel.HasUnsavedChanges;
|
||||||
|
_mainViewModel.HasUnsavedChanges = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var imageName in selectedImages)
|
||||||
|
{
|
||||||
|
// Store current image
|
||||||
|
var currentImage = _mainViewModel.SelectedImage;
|
||||||
|
|
||||||
|
// Change to target image and wait for UI update
|
||||||
|
_mainViewModel.SelectedImage = imageName;
|
||||||
|
await _mainViewModel.WaitForUIUpdateAsync();
|
||||||
|
|
||||||
|
// Analyze current page
|
||||||
|
var items = AnalyzePage();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
item.Source_Image = imageName;
|
||||||
|
MatrixItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore original image if needed
|
||||||
|
if (currentImage != imageName)
|
||||||
|
{
|
||||||
|
_mainViewModel.SelectedImage = currentImage;
|
||||||
|
await _mainViewModel.WaitForUIUpdateAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveColumnConflicts();
|
||||||
|
UpdateMatrixPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MatrixItem> AnalyzePage()
|
||||||
|
{
|
||||||
|
var items = new List<MatrixItem>();
|
||||||
|
|
||||||
var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables
|
var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables
|
||||||
.OfType<osBuscarCoincidencias>()
|
.OfType<osBuscarCoincidencias>()
|
||||||
.Where(tag => tag.Show_On_This_Page)
|
.Where(tag => tag.Show_On_This_Page)
|
||||||
|
@ -173,7 +241,7 @@ namespace CtrEditor.PopUps
|
||||||
foreach (var tag in osExtraccionTagBaseFix_List)
|
foreach (var tag in osExtraccionTagBaseFix_List)
|
||||||
{
|
{
|
||||||
tag.CaptureImageAreaAndDoOCR();
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
MatrixItems.Add(new MatrixItem
|
items.Add(new MatrixItem
|
||||||
{
|
{
|
||||||
TagName = tag.Nombre,
|
TagName = tag.Nombre,
|
||||||
ColumnName = tag.Collumn_name,
|
ColumnName = tag.Collumn_name,
|
||||||
|
@ -181,7 +249,7 @@ namespace CtrEditor.PopUps
|
||||||
Value = tag.Tag_extract,
|
Value = tag.Tag_extract,
|
||||||
Type = "Fixed",
|
Type = "Fixed",
|
||||||
IsCloned = false,
|
IsCloned = false,
|
||||||
SourceObject = tag
|
Id = tag.Id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +257,7 @@ namespace CtrEditor.PopUps
|
||||||
foreach (var tag in osExtraccionTagBaseGrouped_List)
|
foreach (var tag in osExtraccionTagBaseGrouped_List)
|
||||||
{
|
{
|
||||||
tag.CaptureImageAreaAndDoOCR();
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
MatrixItems.Add(new MatrixItem
|
items.Add(new MatrixItem
|
||||||
{
|
{
|
||||||
TagName = tag.Nombre,
|
TagName = tag.Nombre,
|
||||||
ColumnName = tag.Collumn_name,
|
ColumnName = tag.Collumn_name,
|
||||||
|
@ -197,7 +265,7 @@ namespace CtrEditor.PopUps
|
||||||
Value = tag.Tag_extract,
|
Value = tag.Tag_extract,
|
||||||
Type = $"Grouped ({tag.Id_Search_Templates})",
|
Type = $"Grouped ({tag.Id_Search_Templates})",
|
||||||
IsCloned = false,
|
IsCloned = false,
|
||||||
SourceObject = tag
|
Id = tag.Id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +273,7 @@ namespace CtrEditor.PopUps
|
||||||
foreach (var tag in osExtraccionTagCloned_List)
|
foreach (var tag in osExtraccionTagCloned_List)
|
||||||
{
|
{
|
||||||
tag.CaptureImageAreaAndDoOCR();
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
MatrixItems.Add(new MatrixItem
|
items.Add(new MatrixItem
|
||||||
{
|
{
|
||||||
TagName = tag.Nombre,
|
TagName = tag.Nombre,
|
||||||
ColumnName = tag.Collumn_name,
|
ColumnName = tag.Collumn_name,
|
||||||
|
@ -213,20 +281,12 @@ namespace CtrEditor.PopUps
|
||||||
Value = tag.Tag_extract,
|
Value = tag.Tag_extract,
|
||||||
Type = "Cloned",
|
Type = "Cloned",
|
||||||
IsCloned = true,
|
IsCloned = true,
|
||||||
SourceObject = tag
|
Id = tag.Id,
|
||||||
|
Copy_Number = tag.Copy_Number // Add this line
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ordenar los items por número de columna
|
return items;
|
||||||
var items = MatrixItems.OrderBy(x => x.ColumnNumber).ToList();
|
|
||||||
MatrixItems.Clear();
|
|
||||||
foreach (var item in items)
|
|
||||||
{
|
|
||||||
MatrixItems.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResolveColumnConflicts(); // Analizar y resolver conflictos al cargar
|
|
||||||
UpdateMatrixPreview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnMatrixItemsChanged(ObservableCollection<MatrixItem> value)
|
partial void OnMatrixItemsChanged(ObservableCollection<MatrixItem> value)
|
||||||
|
@ -249,7 +309,7 @@ namespace CtrEditor.PopUps
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void ApplyChanges()
|
private async void ApplyChanges()
|
||||||
{
|
{
|
||||||
// Resolver solo los conflictos reales, mantener el orden existente
|
// Resolver solo los conflictos reales, mantener el orden existente
|
||||||
var conflicts = MatrixItems
|
var conflicts = MatrixItems
|
||||||
|
@ -273,16 +333,53 @@ namespace CtrEditor.PopUps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplicar cambios a los objetos
|
bool originalHasUnsavedChanges = _mainViewModel.HasUnsavedChanges;
|
||||||
foreach (var item in MatrixItems)
|
_mainViewModel.HasUnsavedChanges = false;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (item.SourceObject is osExtraccionTag tag)
|
foreach (var imageName in selectedImages)
|
||||||
|
{
|
||||||
|
// Store current image
|
||||||
|
var currentImage = _mainViewModel.SelectedImage;
|
||||||
|
|
||||||
|
// Change to target image and wait for UI update
|
||||||
|
_mainViewModel.SelectedImage = imageName;
|
||||||
|
await Task.Yield();
|
||||||
|
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
|
||||||
|
|
||||||
|
// Apply changes for current page
|
||||||
|
var pageItems = MatrixItems.Where(x => x.Source_Image == imageName);
|
||||||
|
foreach (var item in pageItems)
|
||||||
|
{
|
||||||
|
// Find the tag by Id instead of direct reference
|
||||||
|
var tag = _mainViewModel.ObjetosSimulables
|
||||||
|
.OfType<osExtraccionTag>()
|
||||||
|
.FirstOrDefault(t => t.Id == item.Id);
|
||||||
|
|
||||||
|
if (tag != null)
|
||||||
{
|
{
|
||||||
tag.Collumn_name = item.ColumnName;
|
tag.Collumn_name = item.ColumnName;
|
||||||
tag.Collumn_number = item.ColumnNumber;
|
tag.Collumn_number = item.ColumnNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_mainViewModel.HasUnsavedChanges = true;
|
|
||||||
|
// Restore original image if needed
|
||||||
|
if (currentImage != imageName)
|
||||||
|
{
|
||||||
|
_mainViewModel.SelectedImage = currentImage;
|
||||||
|
await Task.Yield();
|
||||||
|
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mainViewModel.SaveStateObjetosSimulables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges;
|
||||||
|
}
|
||||||
|
|
||||||
_window.Close();
|
_window.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,5 +456,163 @@ namespace CtrEditor.PopUps
|
||||||
|
|
||||||
return nextNumber;
|
return nextNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ExportToExcel()
|
||||||
|
{
|
||||||
|
var saveDialog = new Microsoft.Win32.SaveFileDialog
|
||||||
|
{
|
||||||
|
DefaultExt = ".xlsx",
|
||||||
|
Filter = "Excel files (*.xlsx)|*.xlsx",
|
||||||
|
InitialDirectory = EstadoPersistente.Instance.directorio,
|
||||||
|
FileName = "MatrixExport.xlsx"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveDialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var workbook = new XLWorkbook())
|
||||||
|
{
|
||||||
|
var worksheet = workbook.Worksheets.Add("Matrix");
|
||||||
|
|
||||||
|
// Write headers
|
||||||
|
int colIndex = 1;
|
||||||
|
worksheet.Cell(1, colIndex++).Value = "Image";
|
||||||
|
|
||||||
|
var columns = MatrixItems
|
||||||
|
.GroupBy(x => x.ColumnNumber)
|
||||||
|
.OrderBy(g => g.Key)
|
||||||
|
.Select(g => g.First())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var col in columns)
|
||||||
|
{
|
||||||
|
worksheet.Cell(1, colIndex++).Value = col.ColumnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
int rowIndex = 2;
|
||||||
|
foreach (var row in MatrixRows)
|
||||||
|
{
|
||||||
|
colIndex = 1;
|
||||||
|
worksheet.Cell(rowIndex, colIndex++).Value = row["Image"];
|
||||||
|
|
||||||
|
foreach (var col in columns)
|
||||||
|
{
|
||||||
|
var value = row.ContainsKey(col.ColumnNumber.ToString())
|
||||||
|
? row[col.ColumnNumber.ToString()]
|
||||||
|
: string.Empty;
|
||||||
|
worksheet.Cell(rowIndex, colIndex++).Value = value;
|
||||||
|
}
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format headers
|
||||||
|
var headerRow = worksheet.Row(1);
|
||||||
|
headerRow.Style.Font.Bold = true;
|
||||||
|
headerRow.Style.Fill.BackgroundColor = XLColor.LightGray;
|
||||||
|
headerRow.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
||||||
|
|
||||||
|
// Auto-fit columns
|
||||||
|
worksheet.Columns().AdjustToContents();
|
||||||
|
|
||||||
|
workbook.SaveAs(saveDialog.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show("Export completed successfully", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error exporting to Excel: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task AICorrectMatrix()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_llmService == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("LLM Service not initialized", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnNames = MatrixItems
|
||||||
|
.Select(x => x.ColumnName)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var dialog = new ColumnSelectionDialog(columnNames);
|
||||||
|
if (dialog.ShowDialog() != true) return;
|
||||||
|
|
||||||
|
var sourceColumn = dialog.SelectedSourceColumn;
|
||||||
|
var targetColumn = dialog.SelectedTargetColumn;
|
||||||
|
var sourceLanguage = dialog.SelectedSourceLanguage;
|
||||||
|
var targetLanguage = dialog.SelectedTargetLanguage;
|
||||||
|
|
||||||
|
// Group items by image and process each row
|
||||||
|
var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image);
|
||||||
|
int batchSize = 10;
|
||||||
|
int processedCount = 0;
|
||||||
|
|
||||||
|
foreach (var imageGroup in itemsByImage)
|
||||||
|
{
|
||||||
|
var textPairs = new List<(string Source, string Target)>();
|
||||||
|
var itemsToUpdate = new List<(MatrixItem SourceItem, MatrixItem TargetItem)>();
|
||||||
|
|
||||||
|
// Create pairs for each row
|
||||||
|
var sourceItems = imageGroup
|
||||||
|
.Where(x => x.ColumnName == sourceColumn)
|
||||||
|
.OrderBy(x => x.Copy_Number);
|
||||||
|
|
||||||
|
var targetItems = imageGroup
|
||||||
|
.Where(x => x.ColumnName == targetColumn)
|
||||||
|
.OrderBy(x => x.Copy_Number);
|
||||||
|
|
||||||
|
foreach (var (source, target) in sourceItems.Zip(targetItems, (s, t) => (s, t)))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(source.Value) && !string.IsNullOrWhiteSpace(target.Value))
|
||||||
|
{
|
||||||
|
textPairs.Add((source.Value, target.Value));
|
||||||
|
itemsToUpdate.Add((source, target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process in batches
|
||||||
|
for (int i = 0; i < textPairs.Count; i += batchSize)
|
||||||
|
{
|
||||||
|
var batchPairs = textPairs.Skip(i).Take(batchSize).ToList();
|
||||||
|
var batchItems = itemsToUpdate.Skip(i).Take(batchSize).ToList();
|
||||||
|
|
||||||
|
var correctedPairs = await _llmService.ProcessTextBatch(batchPairs, sourceLanguage, targetLanguage);
|
||||||
|
|
||||||
|
// Update values
|
||||||
|
for (int j = 0; j < correctedPairs.Count; j++)
|
||||||
|
{
|
||||||
|
batchItems[j].SourceItem.Value = correctedPairs[j].Source;
|
||||||
|
batchItems[j].TargetItem.Value = correctedPairs[j].Target;
|
||||||
|
processedCount += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateMatrixPreview();
|
||||||
|
MessageBox.Show($"AI Correction completed. Processed {processedCount} items.",
|
||||||
|
"Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error during AI correction: {ex.Message}",
|
||||||
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~MatrixPreviewViewModel()
|
||||||
|
{
|
||||||
|
_llmService?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,18 @@ using System.Security.Permissions;
|
||||||
|
|
||||||
namespace CtrEditor
|
namespace CtrEditor
|
||||||
{
|
{
|
||||||
|
public class ColumnLanguageMapping
|
||||||
|
{
|
||||||
|
public string ColumnName { get; set; }
|
||||||
|
public string Language { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ColumnPairMapping
|
||||||
|
{
|
||||||
|
public string SourceColumn { get; set; }
|
||||||
|
public string TargetColumn { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
internal class EstadoPersistente
|
internal class EstadoPersistente
|
||||||
{
|
{
|
||||||
// Ruta donde se guardará el estado
|
// Ruta donde se guardará el estado
|
||||||
|
@ -28,6 +40,10 @@ namespace CtrEditor
|
||||||
|
|
||||||
public List<string> RecentDirectories { get; set; } = new List<string>();
|
public List<string> RecentDirectories { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public List<ColumnLanguageMapping> ColumnLanguages { get; set; } = new List<ColumnLanguageMapping>();
|
||||||
|
|
||||||
|
public List<ColumnPairMapping> ColumnPairs { get; set; } = new List<ColumnPairMapping>();
|
||||||
|
|
||||||
// Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
|
// Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
|
||||||
public string directorio
|
public string directorio
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue