Allineamento dati con repository di origine

This commit is contained in:
2025-08-17 23:40:09 +02:00
parent 93f3d02f32
commit f767fe6e35
55 changed files with 48638 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using RestSharp;
namespace HorseRacingPredictor.Manager
{
internal class API
{
// Intervallo tra le chiamate API per evitare limitazioni di rate
private const int DefaultApiCallDelay = 6000;
/// <summary>
/// Esegue una chiamata API generica e restituisce la risposta
/// </summary>
public static RestResponse ExecuteApiRequest(string url, string apiKey, string apiKeyHeader, string hostHeader, string hostValue, int delay = DefaultApiCallDelay)
{
var clientApi = new RestClient(url);
var request = new RestRequest();
request.AddHeader(apiKeyHeader, apiKey);
request.AddHeader(hostHeader, hostValue);
try
{
var response = clientApi.Execute(request);
if (!response.IsSuccessful)
{
throw new Exception($"Errore nella richiesta API: {response.ErrorMessage}");
}
// Aggiungi una pausa tra una chiamata e l'altra
if (delay > 0)
{
Thread.Sleep(delay);
}
return response;
}
catch (Exception ex)
{
throw new Exception($"Errore durante la richiesta API: {ex.Message}", ex);
}
}
/// <summary>
/// Esegue una chiamata API generica con informazioni di tipo per il salvataggio nel database
/// </summary>
public static RestResponse ExecuteApiRequest(string url, string apiKey, string apiKeyHeader, string hostHeader, string hostValue,
string apiType, string parameters, int delay = DefaultApiCallDelay)
{
// La logica è la stessa ma ora passa anche i parametri necessari per salvare nel database
return ExecuteApiRequest(url, apiKey, apiKeyHeader, hostHeader, hostValue, delay);
}
/// <summary>
/// Versione asincrona di ExecuteApiRequest
/// </summary>
public static async Task<RestResponse> ExecuteApiRequestAsync(string url, string apiKey, string apiKeyHeader, string hostHeader, string hostValue, int delay = DefaultApiCallDelay)
{
var clientApi = new RestClient(url);
var request = new RestRequest();
request.AddHeader(apiKeyHeader, apiKey);
request.AddHeader(hostHeader, hostValue);
try
{
var response = await clientApi.ExecuteAsync(request);
if (!response.IsSuccessful)
{
throw new Exception($"Errore nella richiesta API: {response.ErrorMessage}");
}
// Aggiungi una pausa tra una chiamata e l'altra
if (delay > 0)
{
await Task.Delay(delay);
}
return response;
}
catch (Exception ex)
{
throw new Exception($"Errore durante la richiesta API: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di ExecuteApiRequest con informazioni di tipo
/// </summary>
public static async Task<RestResponse> ExecuteApiRequestAsync(string url, string apiKey, string apiKeyHeader, string hostHeader, string hostValue,
string apiType, string parameters, int delay = DefaultApiCallDelay)
{
// La logica è la stessa ma ora passa anche i parametri necessari per salvare nel database
return await ExecuteApiRequestAsync(url, apiKey, apiKeyHeader, hostHeader, hostValue, delay);
}
/// <summary>
/// Salva la risposta dell'API su file per debugging
/// </summary>
public static void SaveResponseToFile(string url, string content, string baseFolderName = "ApiResponses")
{
try
{
string folderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, baseFolderName);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
string fileName = Path.Combine(folderPath, $"ApiResponse_{DateTime.Now:yyyyMMdd_HHmmss}.json");
File.WriteAllText(fileName, content);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il salvataggio della risposta API: {ex.Message}", ex);
}
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Data.SqlClient;
namespace HorseRacingPredictor.Manager
{
internal abstract class Database
{
// La stringa di connessione viene rimossa da qui e definita nelle classi derivate
protected abstract string _connectionString { get; }
protected SqlConnection GetConnection()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
public void LogError(string operation, Exception ex)
{
var message = ex != null ? ex.Message : "Nessuna eccezione fornita";
Console.WriteLine($"Errore durante {operation}: {message}");
}
/// <summary>
/// Esegue una query SQL e gestisce le eccezioni in modo centralizzato
/// </summary>
public void ExecuteQuery(string operation, Action<SqlConnection> action)
{
try
{
using (var connection = GetConnection())
{
action(connection);
}
}
catch (Exception ex)
{
LogError(operation, ex);
}
}
/// <summary>
/// Esegue una query SQL all'interno di una transazione e gestisce le eccezioni
/// </summary>
public void ExecuteTransactionalQuery(string operation, Action<SqlConnection, SqlTransaction> action)
{
try
{
using (var connection = GetConnection())
using (var transaction = connection.BeginTransaction())
{
try
{
action(connection, transaction);
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
LogError($"{operation} (transazione rollback)", ex);
throw; // Rilancia l'eccezione per gestione di livello superiore
}
}
}
catch (Exception ex)
{
LogError(operation, ex);
}
}
/// <summary>
/// Metodo per verificare se la connessione al database è valida
/// </summary>
public bool TestConnection()
{
try
{
using (var connection = GetConnection())
{
return connection.State == System.Data.ConnectionState.Open;
}
}
catch (Exception ex)
{
LogError("test della connessione", ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,293 @@
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Manager
{
/// <summary>
/// Gestore generico per la lettura di file CSV
/// </summary>
public class FileReader
{
public class NullableDecimalConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
return ConvertToDecimal(text);
}
}
public class NullableIntConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
return ConvertToInt(text);
}
}
/// <summary>
/// Legge un file CSV e lo converte in un DataTable
/// </summary>
/// <param name="filePath">Percorso del file CSV</param>
/// <returns>DataTable contenente i dati del file CSV</returns>
public DataTable ReadCsvToDataTable(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
}
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"File non trovato: {filePath}");
}
var dataTable = new DataTable();
// Configurazione per CsvHelper
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true,
MissingFieldFound = null, // Ignora campi mancanti
TrimOptions = TrimOptions.Trim, // Rimuove spazi iniziali e finali
PrepareHeaderForMatch = args => args.Header.Replace("#", "").Trim() // Rimuove # e spazi dai nomi delle colonne
};
using (var reader = new StreamReader(filePath))
using (var csv = new CsvReader(reader, config))
{
// Legge l'intestazione
csv.Read();
csv.ReadHeader();
// Crea le colonne della tabella dai nomi delle colonne del CSV
foreach (var header in csv.HeaderRecord)
{
string columnName = header.Replace("#", "").Trim();
dataTable.Columns.Add(columnName);
}
// Legge i dati riga per riga
while (csv.Read())
{
var row = dataTable.NewRow();
for (int i = 0; i < csv.HeaderRecord.Length; i++)
{
string columnName = csv.HeaderRecord[i].Replace("#", "").Trim();
string value = csv.GetField(i) ?? string.Empty;
row[columnName] = value;
}
dataTable.Rows.Add(row);
}
}
return dataTable;
}
/// <summary>
/// Ottiene una lista di tutti i file CSV in una cartella e nelle sue sottocartelle
/// </summary>
/// <param name="folderPath">Percorso della cartella da esplorare</param>
/// <returns>Lista di percorsi dei file CSV trovati</returns>
public List<string> GetCsvFiles(string folderPath)
{
if (string.IsNullOrEmpty(folderPath))
{
throw new ArgumentException("Il percorso della cartella non può essere vuoto.", nameof(folderPath));
}
if (!Directory.Exists(folderPath))
{
throw new DirectoryNotFoundException($"Directory non trovata: {folderPath}");
}
var csvFiles = Directory.GetFiles(folderPath, "*.csv", SearchOption.AllDirectories);
return new List<string>(csvFiles);
}
/// <summary>
/// Metodo helper per estrarre un valore stringa da una riga di DataTable
/// </summary>
public string GetStringValue(DataRow row, string columnName)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
{
return string.Empty;
}
return row[columnName].ToString();
}
/// <summary>
/// Metodo helper per estrarre un valore intero da una riga di DataTable
/// Gestisce valori nulli o non validi restituendo 0
/// </summary>
public int GetIntValue(DataRow row, string columnName)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
{
return 0;
}
if (int.TryParse(row[columnName].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out int result))
{
return result;
}
return 0;
}
/// <summary>
/// Metodo helper per estrarre un valore intero nullable da una riga di DataTable
/// Restituisce null per valori mancanti o non validi
/// </summary>
public int? GetNullableIntValue(DataRow row, string columnName)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
{
return null;
}
string stringValue = row[columnName].ToString();
if (string.IsNullOrWhiteSpace(stringValue))
{
return null;
}
if (int.TryParse(stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out int result))
{
return result;
}
return null;
}
/// <summary>
/// Metodo helper per estrarre un valore decimale da una riga di DataTable con controllo di precisione
/// Gestisce valori nulli o non validi restituendo 0
/// </summary>
public decimal GetDecimalValue(DataRow row, string columnName, int precision, int scale)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
{
return 0.0M;
}
if (decimal.TryParse(row[columnName].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out decimal result))
{
// Arrotonda il valore per rispettare il limite di precisione e scala
decimal factor = (decimal)Math.Pow(10, scale);
result = Math.Round(result, scale);
// Verifica se il valore è entro i limiti di precisione
decimal maxValue = (decimal)Math.Pow(10, precision - scale) - (1 / factor);
if (Math.Abs(result) > maxValue)
{
Console.WriteLine($"Valore fuori range per {columnName}: {result}, limitato a {maxValue}");
result = result < 0 ? -maxValue : maxValue;
}
return result;
}
return 0.0M;
}
/// <summary>
/// Metodo helper per estrarre un valore decimale nullable da una riga di DataTable
/// Restituisce null per valori mancanti o non validi
/// </summary>
public decimal? GetNullableDecimalValue(DataRow row, string columnName, int precision, int scale)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
{
return null;
}
string stringValue = row[columnName].ToString();
if (string.IsNullOrWhiteSpace(stringValue))
{
return null;
}
if (decimal.TryParse(stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal result))
{
// Arrotonda il valore per rispettare il limite di precisione e scala
decimal factor = (decimal)Math.Pow(10, scale);
result = Math.Round(result, scale);
// Verifica se il valore è entro i limiti di precisione
decimal maxValue = (decimal)Math.Pow(10, precision - scale) - (1 / factor);
if (Math.Abs(result) > maxValue)
{
Console.WriteLine($"Valore fuori range per {columnName}: {result}, limitato a {maxValue}");
result = result < 0 ? -maxValue : maxValue;
}
return result;
}
return null;
}
/// <summary>
/// Converte un valore stringa in int con gestione dei valori nulli o non validi
/// </summary>
public static int ConvertToInt(string value)
{
if (string.IsNullOrWhiteSpace(value))
return 0;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out int result))
return result;
return 0;
}
/// <summary>
/// Converte un valore stringa in decimal con gestione dei valori nulli o non validi
/// </summary>
public static decimal ConvertToDecimal(string value)
{
if (string.IsNullOrWhiteSpace(value))
return 0m;
if (decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal result))
return result;
return 0m;
}
/// <summary>
/// Converte un valore stringa in int? con gestione dei valori nulli o non validi
/// </summary>
public static int? ConvertToNullableInt(string value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out int result))
return result;
return null;
}
/// <summary>
/// Converte un valore stringa in decimal? con gestione dei valori nulli o non validi
/// </summary>
public static decimal? ConvertToNullableDecimal(string value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
if (decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal result))
return result;
return null;
}
}
}