Transizione completa da WinForms a WPF con interfaccia moderna (sidebar, pagine, palette Catppuccin Mocha). Aggiunta integrazione con The Racing API per le corse dei cavalli, inclusi nuovi moduli di backend e parsing dati. Introdotti componenti UI personalizzati (CardPanel, ModernButton, ModernProgressBar, NavButton, ModernTheme) per un look coerente e moderno. Gestione avanzata delle impostazioni, esportazione CSV migliorata, refactoring della logica di business e maggiore robustezza nelle chiamate API. Aggiunti stub CsvHelper e file di progetto di backup per facilitare la compatibilità e la migrazione.
267 lines
9.9 KiB
C#
267 lines
9.9 KiB
C#
using System;
|
|
using System.Data;
|
|
using System.Text.Json;
|
|
using HorseRacingPredictor.HorseRacing.API;
|
|
|
|
namespace HorseRacingPredictor.HorseRacing
|
|
{
|
|
/// <summary>
|
|
/// Gestore centralizzato per la sezione Corse dei Cavalli.
|
|
/// Scarica i dati da The Racing API e li converte in DataTable.
|
|
/// </summary>
|
|
public class Main
|
|
{
|
|
private RacingApiClient _client;
|
|
|
|
public Main(string username, string password)
|
|
{
|
|
_client = new RacingApiClient(username, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggiorna le credenziali API
|
|
/// </summary>
|
|
public void UpdateCredentials(string username, string password)
|
|
{
|
|
_client = new RacingApiClient(username, password);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scarica le racecard (programma corse) per oggi o domani e le restituisce come DataTable
|
|
/// </summary>
|
|
public DataTable GetRacecards(string day, IProgress<int> progress = null, IProgress<string> status = null)
|
|
{
|
|
try
|
|
{
|
|
status?.Report("Connessione a The Racing API...");
|
|
progress?.Report(10);
|
|
|
|
var response = _client.GetRacecardsFree(day);
|
|
progress?.Report(60);
|
|
|
|
status?.Report("Elaborazione racecard...");
|
|
var table = ParseRacecardsResponse(response.Content);
|
|
progress?.Report(100);
|
|
|
|
status?.Report($"Trovate {table.Rows.Count} corse");
|
|
return table;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
status?.Report($"Errore: {ex.Message}");
|
|
return CreateEmptyRacecardsTable();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scarica i risultati per un intervallo di date
|
|
/// </summary>
|
|
public DataTable GetResults(DateTime startDate, DateTime endDate,
|
|
IProgress<int> progress = null, IProgress<string> status = null)
|
|
{
|
|
try
|
|
{
|
|
status?.Report("Scaricamento risultati...");
|
|
progress?.Report(10);
|
|
|
|
var response = _client.GetResults(startDate, endDate);
|
|
progress?.Report(60);
|
|
|
|
status?.Report("Elaborazione risultati...");
|
|
var table = ParseResultsResponse(response.Content);
|
|
progress?.Report(100);
|
|
|
|
status?.Report($"Trovati {table.Rows.Count} risultati");
|
|
return table;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
status?.Report($"Errore: {ex.Message}");
|
|
return CreateEmptyResultsTable();
|
|
}
|
|
}
|
|
|
|
#region DataTable creation
|
|
|
|
private DataTable CreateEmptyRacecardsTable()
|
|
{
|
|
var dt = new DataTable();
|
|
dt.Columns.Add("Ora", typeof(string));
|
|
dt.Columns.Add("Ippodromo", typeof(string));
|
|
dt.Columns.Add("Regione", typeof(string));
|
|
dt.Columns.Add("Corsa", typeof(string));
|
|
dt.Columns.Add("Distanza", typeof(string));
|
|
dt.Columns.Add("Tipo", typeof(string));
|
|
dt.Columns.Add("Classe", typeof(string));
|
|
dt.Columns.Add("Terreno", typeof(string));
|
|
dt.Columns.Add("N. Corridori", typeof(int));
|
|
dt.Columns.Add("Età", typeof(string));
|
|
dt.Columns.Add("Premio", typeof(string));
|
|
return dt;
|
|
}
|
|
|
|
private DataTable CreateEmptyResultsTable()
|
|
{
|
|
var dt = new DataTable();
|
|
dt.Columns.Add("Data", typeof(string));
|
|
dt.Columns.Add("Ippodromo", typeof(string));
|
|
dt.Columns.Add("Corsa", typeof(string));
|
|
dt.Columns.Add("Distanza", typeof(string));
|
|
dt.Columns.Add("Terreno", typeof(string));
|
|
dt.Columns.Add("1° Classificato", typeof(string));
|
|
dt.Columns.Add("2° Classificato", typeof(string));
|
|
dt.Columns.Add("3° Classificato", typeof(string));
|
|
dt.Columns.Add("Fantino 1°", typeof(string));
|
|
dt.Columns.Add("SP 1°", typeof(string));
|
|
return dt;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region JSON Parsing
|
|
|
|
private DataTable ParseRacecardsResponse(string json)
|
|
{
|
|
var dt = CreateEmptyRacecardsTable();
|
|
if (string.IsNullOrEmpty(json)) return dt;
|
|
|
|
try
|
|
{
|
|
using (var doc = JsonDocument.Parse(json))
|
|
{
|
|
var root = doc.RootElement;
|
|
|
|
if (!root.TryGetProperty("racecards", out var racecardsEl))
|
|
return dt;
|
|
|
|
foreach (var rc in racecardsEl.EnumerateArray())
|
|
{
|
|
try
|
|
{
|
|
var row = dt.NewRow();
|
|
|
|
row["Ora"] = GetString(rc, "off_time", "");
|
|
row["Ippodromo"] = GetString(rc, "course", "");
|
|
row["Regione"] = GetString(rc, "region", "");
|
|
row["Corsa"] = GetString(rc, "race_name", "");
|
|
row["Distanza"] = GetString(rc, "distance", "");
|
|
row["Tipo"] = GetString(rc, "type", "");
|
|
row["Classe"] = GetString(rc, "race_class", "");
|
|
row["Terreno"] = GetString(rc, "going", "");
|
|
row["Età"] = GetString(rc, "age_band", "");
|
|
row["Premio"] = GetString(rc, "prize", "");
|
|
|
|
if (rc.TryGetProperty("runners", out var runnersEl) &&
|
|
runnersEl.ValueKind == JsonValueKind.Array)
|
|
{
|
|
row["N. Corridori"] = runnersEl.GetArrayLength();
|
|
}
|
|
else if (rc.TryGetProperty("field_size", out var fsEl) &&
|
|
fsEl.ValueKind == JsonValueKind.Number)
|
|
{
|
|
row["N. Corridori"] = fsEl.GetInt32();
|
|
}
|
|
else
|
|
{
|
|
row["N. Corridori"] = 0;
|
|
}
|
|
|
|
dt.Rows.Add(row);
|
|
}
|
|
catch
|
|
{
|
|
// Salta righe problematiche
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Restituisci tabella vuota in caso di errore di parsing
|
|
}
|
|
|
|
return dt;
|
|
}
|
|
|
|
private DataTable ParseResultsResponse(string json)
|
|
{
|
|
var dt = CreateEmptyResultsTable();
|
|
if (string.IsNullOrEmpty(json)) return dt;
|
|
|
|
try
|
|
{
|
|
using (var doc = JsonDocument.Parse(json))
|
|
{
|
|
var root = doc.RootElement;
|
|
|
|
if (!root.TryGetProperty("results", out var resultsEl))
|
|
return dt;
|
|
|
|
foreach (var res in resultsEl.EnumerateArray())
|
|
{
|
|
try
|
|
{
|
|
var row = dt.NewRow();
|
|
|
|
row["Data"] = GetString(res, "date", "");
|
|
row["Ippodromo"] = GetString(res, "course", "");
|
|
row["Corsa"] = GetString(res, "race_name", "");
|
|
row["Distanza"] = GetString(res, "distance", "");
|
|
row["Terreno"] = GetString(res, "going", "");
|
|
|
|
if (res.TryGetProperty("runners", out var runnersEl) &&
|
|
runnersEl.ValueKind == JsonValueKind.Array)
|
|
{
|
|
int idx = 0;
|
|
foreach (var runner in runnersEl.EnumerateArray())
|
|
{
|
|
var pos = GetString(runner, "position", "");
|
|
if (pos == "1" || idx == 0)
|
|
{
|
|
row["1° Classificato"] = GetString(runner, "horse", "");
|
|
row["Fantino 1°"] = GetString(runner, "jockey", "");
|
|
row["SP 1°"] = GetString(runner, "sp", "");
|
|
}
|
|
else if (pos == "2" || idx == 1)
|
|
{
|
|
row["2° Classificato"] = GetString(runner, "horse", "");
|
|
}
|
|
else if (pos == "3" || idx == 2)
|
|
{
|
|
row["3° Classificato"] = GetString(runner, "horse", "");
|
|
}
|
|
idx++;
|
|
if (idx >= 3) break;
|
|
}
|
|
}
|
|
|
|
dt.Rows.Add(row);
|
|
}
|
|
catch
|
|
{
|
|
// Salta righe problematiche
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Restituisci tabella vuota in caso di errore di parsing
|
|
}
|
|
|
|
return dt;
|
|
}
|
|
|
|
private static string GetString(JsonElement el, string property, string defaultValue)
|
|
{
|
|
if (el.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.String)
|
|
return prop.GetString() ?? defaultValue;
|
|
if (el.TryGetProperty(property, out prop) && prop.ValueKind == JsonValueKind.Number)
|
|
return prop.ToString();
|
|
return defaultValue;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|