Aggiunte opzioni avanzate download calcio API-Football
Introdotta la classe FootballDownloadOptions per la gestione centralizzata delle opzioni di download e filtri. L’utente può ora scegliere quali endpoint scaricare (partite, quote, previsioni, classifiche, H2H, eventi, formazioni, statistiche, infortuni) e impostare parametri avanzati (ID bookmaker, pagine quote, leghe, delay API, quota minima residua, ecc.) tramite la nuova sezione UI. Rifattorizzata la logica di download per supportare il caricamento condizionale e sequenziale degli endpoint, con progress bar proporzionale e callback di stato dettagliate. Aggiunti nuovi client API per endpoint aggiuntivi e metodi di enrichment per arricchire dinamicamente la tabella delle partite. Aggiornate le impostazioni utente e la UI per supportare tutte le nuove opzioni. Aggiunto il controllo della quota API residua prima del download. Inseriti nuovi file sorgente per i client API mancanti e la gestione delle opzioni.
This commit is contained in:
@@ -54,4 +54,9 @@
|
|||||||
<ItemGroup Label="EmbeddedResource items now included by globbing that were not in the original project file">
|
<ItemGroup Label="EmbeddedResource items now included by globbing that were not in the original project file">
|
||||||
<EmbeddedResource Remove="Main.resx" />
|
<EmbeddedResource Remove="Main.resx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="HorseRacingPredictor\appsettings.json" Link="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "fixtures/events" dell'API-Football.
|
||||||
|
/// Restituisce gli eventi di una partita (gol, cartellini, sostituzioni, VAR).
|
||||||
|
/// </summary>
|
||||||
|
internal class Events : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "fixtures/events";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene tutti gli eventi per una partita
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetEventsByFixture(int fixtureId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero degli eventi per la partita {fixtureId}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "fixtures/statistics" dell'API-Football.
|
||||||
|
/// Restituisce le statistiche di una partita (possesso, tiri, falli, ecc.).
|
||||||
|
/// </summary>
|
||||||
|
internal class FixtureStatistics : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "fixtures/statistics";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene le statistiche per una partita
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetStatisticsByFixture(int fixtureId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero delle statistiche per la partita {fixtureId}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "fixtures/headtohead" dell'API-Football.
|
||||||
|
/// Restituisce lo storico degli scontri diretti tra due squadre.
|
||||||
|
/// </summary>
|
||||||
|
internal class HeadToHead : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "fixtures/headtohead";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene gli scontri diretti tra due squadre
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="teamId1">ID della prima squadra</param>
|
||||||
|
/// <param name="teamId2">ID della seconda squadra</param>
|
||||||
|
/// <param name="last">Numero di ultimi scontri da recuperare (default 5)</param>
|
||||||
|
public RestResponse GetH2H(int teamId1, int teamId2, int last = 5)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?h2h={teamId1}-{teamId2}&last={last}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero H2H per {teamId1} vs {teamId2}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "injuries" dell'API-Football.
|
||||||
|
/// Restituisce la lista di giocatori infortunati o squalificati.
|
||||||
|
/// </summary>
|
||||||
|
internal class Injuries : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "injuries";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene gli infortunati per una data specifica
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetInjuriesByDate(DateTime date)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string dateStr = date.ToString("yyyy-MM-dd");
|
||||||
|
return ExecuteRequest($"{Endpoint}?date={dateStr}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero degli infortunati per la data {date.ToShortDateString()}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene gli infortunati per una partita specifica
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetInjuriesByFixture(int fixtureId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero degli infortunati per la partita {fixtureId}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene gli infortunati per una lega e stagione
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetInjuriesByLeague(int leagueId, int season)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?league={leagueId}&season={season}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero degli infortunati per lega {leagueId}, stagione {season}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "fixtures/lineups" dell'API-Football.
|
||||||
|
/// Restituisce formazioni, modulo e titolari di una partita.
|
||||||
|
/// </summary>
|
||||||
|
internal class Lineups : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "fixtures/lineups";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene le formazioni per una partita
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetLineupsByFixture(int fixtureId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}", ApiTypes.Fixtures);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero delle formazioni per la partita {fixtureId}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "standings" dell'API-Football.
|
||||||
|
/// Restituisce la classifica di un campionato per una stagione.
|
||||||
|
/// </summary>
|
||||||
|
internal class Standings : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "standings";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene la classifica per una lega e una stagione
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetStandings(int leagueId, int season)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?league={leagueId}&season={season}", ApiTypes.Leagues);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero della classifica per lega {leagueId}, stagione {season}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene la classifica per un team e una stagione
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetStandingsByTeam(int teamId, int season)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest($"{Endpoint}?team={teamId}&season={season}", ApiTypes.Teams);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero della classifica per team {teamId}, stagione {season}: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client per l'endpoint "status" dell'API-Football.
|
||||||
|
/// Restituisce la quota residua giornaliera e le informazioni sull'account.
|
||||||
|
/// Non conta come chiamata nella quota giornaliera.
|
||||||
|
/// </summary>
|
||||||
|
internal class Status : HorseRacingPredictor.Football.Manager.API
|
||||||
|
{
|
||||||
|
private const string Endpoint = "status";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene lo stato dell'account e la quota residua
|
||||||
|
/// </summary>
|
||||||
|
public RestResponse GetStatus()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ExecuteRequest(Endpoint);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Errore durante il recupero dello stato API: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace HorseRacingPredictor.Football
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parametri configurabili per controllare quali endpoint API-Football scaricare
|
||||||
|
/// e come filtrare i dati risultanti.
|
||||||
|
/// </summary>
|
||||||
|
public class FootballDownloadOptions
|
||||||
|
{
|
||||||
|
// ?? Endpoint da scaricare ?????????????????????????????????
|
||||||
|
public bool DownloadFixtures { get; set; } = true;
|
||||||
|
public bool DownloadOdds { get; set; } = true;
|
||||||
|
public bool DownloadPredictions { get; set; } = true;
|
||||||
|
public bool DownloadStandings { get; set; } = false;
|
||||||
|
public bool DownloadH2H { get; set; } = false;
|
||||||
|
public bool DownloadEvents { get; set; } = false;
|
||||||
|
public bool DownloadLineups { get; set; } = false;
|
||||||
|
public bool DownloadStatistics { get; set; } = false;
|
||||||
|
public bool DownloadInjuries { get; set; } = false;
|
||||||
|
|
||||||
|
// ?? Filtri ????????????????????????????????????????????????
|
||||||
|
/// <summary>
|
||||||
|
/// Se non vuoto, scarica fixture solo per queste leghe (league IDs).
|
||||||
|
/// Lista vuota = tutte le leghe.
|
||||||
|
/// </summary>
|
||||||
|
public List<int> LeagueIds { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID del bookmaker per le quote (default 8 = Bet365).
|
||||||
|
/// </summary>
|
||||||
|
public int BookmakerId { get; set; } = 8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Numero massimo di pagine da scaricare per le quote.
|
||||||
|
/// </summary>
|
||||||
|
public int OddsMaxPages { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timezone IANA per le date delle fixture (es. "Europe/Rome").
|
||||||
|
/// </summary>
|
||||||
|
public string Timezone { get; set; } = "Europe/Rome";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stagione corrente per le classifiche (calcolata automaticamente se 0).
|
||||||
|
/// </summary>
|
||||||
|
public int Season { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Numero massimo di fixture per cui scaricare dati aggiuntivi
|
||||||
|
/// (H2H, events, lineups, statistics) per evitare troppe chiamate.
|
||||||
|
/// 0 = nessun limite.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxFixturesForDetails { get; set; } = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ritardo in millisecondi tra una chiamata API e l'altra per rispettare il rate limit.
|
||||||
|
/// </summary>
|
||||||
|
public int ApiDelayMs { get; set; } = 300;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Se true, controlla la quota residua prima di iniziare e si ferma quando insufficiente.
|
||||||
|
/// </summary>
|
||||||
|
public bool CheckQuota { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Numero minimo di richieste residue prima di fermare il download.
|
||||||
|
/// </summary>
|
||||||
|
public int MinRemainingQuota { get; set; } = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restituisce la stagione corrente calcolata (anno corrente o anno-1 se prima di luglio).
|
||||||
|
/// </summary>
|
||||||
|
public int GetEffectiveSeason()
|
||||||
|
{
|
||||||
|
if (Season > 0) return Season;
|
||||||
|
var now = System.DateTime.Now;
|
||||||
|
return now.Month >= 7 ? now.Year : now.Year - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Conta il numero approssimativo di chiamate API previste per una data.
|
||||||
|
/// Utile per mostrare all'utente una stima.
|
||||||
|
/// </summary>
|
||||||
|
public int EstimateApiCalls(int fixtureCount)
|
||||||
|
{
|
||||||
|
int calls = 0;
|
||||||
|
if (DownloadFixtures) calls += 1;
|
||||||
|
if (DownloadOdds) calls += OddsMaxPages;
|
||||||
|
if (DownloadPredictions) calls += fixtureCount;
|
||||||
|
if (DownloadStandings && LeagueIds.Count > 0) calls += LeagueIds.Count;
|
||||||
|
else if (DownloadStandings) calls += 5; // stima
|
||||||
|
if (DownloadH2H) calls += fixtureCount;
|
||||||
|
if (DownloadEvents) calls += fixtureCount;
|
||||||
|
if (DownloadLineups) calls += fixtureCount;
|
||||||
|
if (DownloadStatistics) calls += fixtureCount;
|
||||||
|
if (DownloadInjuries) calls += 1;
|
||||||
|
if (CheckQuota) calls += 1; // status endpoint
|
||||||
|
return calls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ namespace HorseRacingPredictor
|
|||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
};
|
};
|
||||||
|
|
||||||
// ?? Football ????????????????????????????????????????????
|
// ?? Football ??????????????????????????????????????????
|
||||||
public string ApiKey { get; set; } = string.Empty;
|
public string ApiKey { get; set; } = string.Empty;
|
||||||
public string FbExportPath { get; set; } = string.Empty;
|
public string FbExportPath { get; set; } = string.Empty;
|
||||||
public string FbPrefix { get; set; } = string.Empty;
|
public string FbPrefix { get; set; } = string.Empty;
|
||||||
@@ -30,7 +30,27 @@ namespace HorseRacingPredictor
|
|||||||
public string FbDateFormat { get; set; } = "yyyy-MM-dd";
|
public string FbDateFormat { get; set; } = "yyyy-MM-dd";
|
||||||
public string FbFormat { get; set; } = "CSV";
|
public string FbFormat { get; set; } = "CSV";
|
||||||
|
|
||||||
// ?? Racing ??????????????????????????????????????????????
|
// ?? Football Download Options ????????????????????????????
|
||||||
|
public bool FbDownloadFixtures { get; set; } = true;
|
||||||
|
public bool FbDownloadOdds { get; set; } = true;
|
||||||
|
public bool FbDownloadPredictions { get; set; } = true;
|
||||||
|
public bool FbDownloadStandings { get; set; } = false;
|
||||||
|
public bool FbDownloadH2H { get; set; } = false;
|
||||||
|
public bool FbDownloadEvents { get; set; } = false;
|
||||||
|
public bool FbDownloadLineups { get; set; } = false;
|
||||||
|
public bool FbDownloadStatistics { get; set; } = false;
|
||||||
|
public bool FbDownloadInjuries { get; set; } = false;
|
||||||
|
public List<int> FbLeagueIds { get; set; } = new();
|
||||||
|
public int FbBookmakerId { get; set; } = 8;
|
||||||
|
public int FbOddsMaxPages { get; set; } = 3;
|
||||||
|
public string FbTimezone { get; set; } = "Europe/Rome";
|
||||||
|
public int FbSeason { get; set; } = 0;
|
||||||
|
public int FbMaxFixturesForDetails { get; set; } = 50;
|
||||||
|
public int FbApiDelayMs { get; set; } = 300;
|
||||||
|
public bool FbCheckQuota { get; set; } = true;
|
||||||
|
public int FbMinRemainingQuota { get; set; } = 10;
|
||||||
|
|
||||||
|
// ?? Racing ???????????????????????????????????????????????
|
||||||
public string RacingApiKey { get; set; } = string.Empty;
|
public string RacingApiKey { get; set; } = string.Empty;
|
||||||
public string RcExportPath { get; set; } = string.Empty;
|
public string RcExportPath { get; set; } = string.Empty;
|
||||||
public string RcPrefix { get; set; } = string.Empty;
|
public string RcPrefix { get; set; } = string.Empty;
|
||||||
@@ -41,7 +61,35 @@ namespace HorseRacingPredictor
|
|||||||
public string RcTimezone { get; set; } = "Australia/Sydney";
|
public string RcTimezone { get; set; } = "Australia/Sydney";
|
||||||
public List<string> RcCountries { get; set; } = new() { "au", "nz" };
|
public List<string> RcCountries { get; set; } = new() { "au", "nz" };
|
||||||
|
|
||||||
// ?? Persistence ?????????????????????????????????????????
|
// ?? Persistence ??????????????????????????????????????
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Costruisce un FootballDownloadOptions a partire dalle impostazioni salvate.
|
||||||
|
/// </summary>
|
||||||
|
public Football.FootballDownloadOptions ToFootballDownloadOptions()
|
||||||
|
{
|
||||||
|
return new Football.FootballDownloadOptions
|
||||||
|
{
|
||||||
|
DownloadFixtures = FbDownloadFixtures,
|
||||||
|
DownloadOdds = FbDownloadOdds,
|
||||||
|
DownloadPredictions = FbDownloadPredictions,
|
||||||
|
DownloadStandings = FbDownloadStandings,
|
||||||
|
DownloadH2H = FbDownloadH2H,
|
||||||
|
DownloadEvents = FbDownloadEvents,
|
||||||
|
DownloadLineups = FbDownloadLineups,
|
||||||
|
DownloadStatistics = FbDownloadStatistics,
|
||||||
|
DownloadInjuries = FbDownloadInjuries,
|
||||||
|
LeagueIds = new List<int>(FbLeagueIds),
|
||||||
|
BookmakerId = FbBookmakerId,
|
||||||
|
OddsMaxPages = FbOddsMaxPages,
|
||||||
|
Timezone = FbTimezone,
|
||||||
|
Season = FbSeason,
|
||||||
|
MaxFixturesForDetails = FbMaxFixturesForDetails,
|
||||||
|
ApiDelayMs = FbApiDelayMs,
|
||||||
|
CheckQuota = FbCheckQuota,
|
||||||
|
MinRemainingQuota = FbMinRemainingQuota
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static UserSettings Load()
|
public static UserSettings Load()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -765,6 +765,85 @@
|
|||||||
Foreground="{StaticResource BrText}"
|
Foreground="{StaticResource BrText}"
|
||||||
Click="btnBrowseFbExport_Click"/>
|
Click="btnBrowseFbExport_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- ?? OPZIONI DOWNLOAD ENDPOINT ?? -->
|
||||||
|
<Border Height="1" Background="{StaticResource BrBorder}" Margin="0,16,0,14"/>
|
||||||
|
<TextBlock Text="Endpoint da scaricare" FontSize="13" FontFamily="Segoe UI Semibold"
|
||||||
|
Foreground="{StaticResource BrText}" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<WrapPanel Orientation="Horizontal">
|
||||||
|
<CheckBox x:Name="chkFbFixtures" IsChecked="True" Margin="0,0,16,6">Partite</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbOdds" IsChecked="True" Margin="0,0,16,6">Quote</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbPredictions" IsChecked="True" Margin="0,0,16,6">Previsioni</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbStandings" Margin="0,0,16,6">Classifiche</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbH2H" Margin="0,0,16,6">Scontri diretti</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbEvents" Margin="0,0,16,6">Eventi</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbLineups" Margin="0,0,16,6">Formazioni</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbStatistics" Margin="0,0,16,6">Statistiche</CheckBox>
|
||||||
|
<CheckBox x:Name="chkFbInjuries" Margin="0,0,16,6">Infortuni</CheckBox>
|
||||||
|
</WrapPanel>
|
||||||
|
|
||||||
|
<!-- ?? PARAMETRI AVANZATI ?? -->
|
||||||
|
<Border Height="1" Background="{StaticResource BrBorder}" Margin="0,10,0,14"/>
|
||||||
|
<TextBlock Text="Parametri avanzati" FontSize="13" FontFamily="Segoe UI Semibold"
|
||||||
|
Foreground="{StaticResource BrText}" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Grid Margin="0,0,0,8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="165"/>
|
||||||
|
<ColumnDefinition Width="22"/>
|
||||||
|
<ColumnDefinition Width="165"/>
|
||||||
|
<ColumnDefinition Width="22"/>
|
||||||
|
<ColumnDefinition Width="165"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="ID Bookmaker" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbBookmakerId" Style="{StaticResource FlatTb}" Text="8"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Max pagine quote" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbOddsMaxPages" Style="{StaticResource FlatTb}" Text="3"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="4">
|
||||||
|
<TextBlock Text="Max fixture dettagli" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbMaxFixtures" Style="{StaticResource FlatTb}" Text="50"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Margin="0,0,0,8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="245"/>
|
||||||
|
<ColumnDefinition Width="22"/>
|
||||||
|
<ColumnDefinition Width="245"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Timezone" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbTimezone" Style="{StaticResource FlatTb}" Text="Europe/Rome"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="ID Leghe (separati da virgola, vuoto = tutte)" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbLeagueIds" Style="{StaticResource FlatTb}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Margin="0,0,0,4">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="22"/>
|
||||||
|
<ColumnDefinition Width="165"/>
|
||||||
|
<ColumnDefinition Width="22"/>
|
||||||
|
<ColumnDefinition Width="165"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<CheckBox x:Name="chkFbCheckQuota" Grid.Column="0" IsChecked="True">Controlla quota</CheckBox>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Quota minima residua" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbMinQuota" Style="{StaticResource FlatTb}" Text="10"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="4">
|
||||||
|
<TextBlock Text="Delay API (ms)" Foreground="{StaticResource BrSubtext0}" FontSize="11" Margin="0,0,0,4"/>
|
||||||
|
<TextBox x:Name="txtFbApiDelay" Style="{StaticResource FlatTb}" Text="300"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -266,23 +266,32 @@ namespace HorseRacingPredictor
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
pbFootball.Value = 0;
|
pbFootball.Value = 0;
|
||||||
lblStatusFb.Text = "Scaricamento elenco partite…";
|
lblStatusFb.Text = "Preparazione opzioni di download…";
|
||||||
btnDownloadFb.IsEnabled = false;
|
btnDownloadFb.IsEnabled = false;
|
||||||
dpFootball.IsEnabled = false;
|
dpFootball.IsEnabled = false;
|
||||||
btnExportFbCsv.IsEnabled = false;
|
btnExportFbCsv.IsEnabled = false;
|
||||||
|
|
||||||
|
// Costruisci le opzioni di download dalla UI
|
||||||
|
var options = BuildFootballDownloadOptions();
|
||||||
|
|
||||||
var progress = new Progress<int>(v => pbFootball.Value = v);
|
var progress = new Progress<int>(v => pbFootball.Value = v);
|
||||||
var status = new Progress<string>(s => lblStatusFb.Text = s);
|
var status = new Progress<string>(s => lblStatusFb.Text = s);
|
||||||
|
|
||||||
var table = await Task.Run(() =>
|
var table = await Task.Run(() =>
|
||||||
_footballManager.GetTodayFixtures(date, progress, status));
|
_footballManager.GetTodayFixtures(date, options, progress, status));
|
||||||
|
|
||||||
_footballData = table;
|
_footballData = table;
|
||||||
|
|
||||||
// Ensure the start time column exists and populate it (no timezone label)
|
// Ensure the start time column exists and populate it (no timezone label)
|
||||||
InjectRomeStartTimeColumn(_footballData, "Inizio");
|
InjectRomeStartTimeColumn(_footballData, "Inizio");
|
||||||
|
|
||||||
|
// Nascondi le colonne interne (ID di supporto) dal DataGrid
|
||||||
dgFootball.ItemsSource = _footballData?.DefaultView;
|
dgFootball.ItemsSource = _footballData?.DefaultView;
|
||||||
|
dgFootball.AutoGeneratingColumn += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName is "LeagueId" or "HomeTeamId" or "AwayTeamId")
|
||||||
|
e.Cancel = true;
|
||||||
|
};
|
||||||
|
|
||||||
if (_footballData != null && _footballData.Rows.Count > 0)
|
if (_footballData != null && _footballData.Rows.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -847,6 +856,26 @@ namespace HorseRacingPredictor
|
|||||||
SetComboBoxSelectionByContent(cmbFbDateFormat, s.FbDateFormat);
|
SetComboBoxSelectionByContent(cmbFbDateFormat, s.FbDateFormat);
|
||||||
SetComboBoxSelectionByContent(cmbFbFormat, s.FbFormat);
|
SetComboBoxSelectionByContent(cmbFbFormat, s.FbFormat);
|
||||||
|
|
||||||
|
// Football Download Options
|
||||||
|
chkFbFixtures.IsChecked = s.FbDownloadFixtures;
|
||||||
|
chkFbOdds.IsChecked = s.FbDownloadOdds;
|
||||||
|
chkFbPredictions.IsChecked = s.FbDownloadPredictions;
|
||||||
|
chkFbStandings.IsChecked = s.FbDownloadStandings;
|
||||||
|
chkFbH2H.IsChecked = s.FbDownloadH2H;
|
||||||
|
chkFbEvents.IsChecked = s.FbDownloadEvents;
|
||||||
|
chkFbLineups.IsChecked = s.FbDownloadLineups;
|
||||||
|
chkFbStatistics.IsChecked = s.FbDownloadStatistics;
|
||||||
|
chkFbInjuries.IsChecked = s.FbDownloadInjuries;
|
||||||
|
txtFbBookmakerId.Text = s.FbBookmakerId.ToString();
|
||||||
|
txtFbOddsMaxPages.Text = s.FbOddsMaxPages.ToString();
|
||||||
|
txtFbMaxFixtures.Text = s.FbMaxFixturesForDetails.ToString();
|
||||||
|
if (txtFbTimezone != null) txtFbTimezone.Text = s.FbTimezone;
|
||||||
|
txtFbLeagueIds.Text = s.FbLeagueIds.Count > 0 ? string.Join(",", s.FbLeagueIds) : "";
|
||||||
|
chkFbCheckQuota.IsChecked = s.FbCheckQuota;
|
||||||
|
txtFbMinQuota.Text = s.FbMinRemainingQuota.ToString();
|
||||||
|
txtFbApiDelay.Text = s.FbApiDelayMs.ToString();
|
||||||
|
|
||||||
|
// Racing
|
||||||
txtRcExportPath.Text = s.RcExportPath;
|
txtRcExportPath.Text = s.RcExportPath;
|
||||||
txtRcPrefix.Text = s.RcPrefix;
|
txtRcPrefix.Text = s.RcPrefix;
|
||||||
txtRcSuffix.Text = s.RcSuffix;
|
txtRcSuffix.Text = s.RcSuffix;
|
||||||
@@ -1091,6 +1120,10 @@ namespace HorseRacingPredictor
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Costruisci e salva le opzioni di download in un FootballDownloadOptions
|
||||||
|
// per validazione prima del salvataggio
|
||||||
|
var options = BuildFootballDownloadOptions();
|
||||||
|
|
||||||
var s = new UserSettings
|
var s = new UserSettings
|
||||||
{
|
{
|
||||||
ApiKey = txtApiKey.Text.Trim(),
|
ApiKey = txtApiKey.Text.Trim(),
|
||||||
@@ -1100,6 +1133,25 @@ namespace HorseRacingPredictor
|
|||||||
FbIncludeDate = chkFbIncludeDate.IsChecked == true,
|
FbIncludeDate = chkFbIncludeDate.IsChecked == true,
|
||||||
FbDateFormat = (cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd",
|
FbDateFormat = (cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd",
|
||||||
FbFormat = (cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV",
|
FbFormat = (cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV",
|
||||||
|
// Football Download Options
|
||||||
|
FbDownloadFixtures = chkFbFixtures.IsChecked == true,
|
||||||
|
FbDownloadOdds = chkFbOdds.IsChecked == true,
|
||||||
|
FbDownloadPredictions = chkFbPredictions.IsChecked == true,
|
||||||
|
FbDownloadStandings = chkFbStandings.IsChecked == true,
|
||||||
|
FbDownloadH2H = chkFbH2H.IsChecked == true,
|
||||||
|
FbDownloadEvents = chkFbEvents.IsChecked == true,
|
||||||
|
FbDownloadLineups = chkFbLineups.IsChecked == true,
|
||||||
|
FbDownloadStatistics = chkFbStatistics.IsChecked == true,
|
||||||
|
FbDownloadInjuries = chkFbInjuries.IsChecked == true,
|
||||||
|
FbBookmakerId = int.TryParse(txtFbBookmakerId.Text.Trim(), out var bId) ? bId : 8,
|
||||||
|
FbOddsMaxPages = int.TryParse(txtFbOddsMaxPages.Text.Trim(), out var omp) ? omp : 3,
|
||||||
|
FbMaxFixturesForDetails = int.TryParse(txtFbMaxFixtures.Text.Trim(), out var mf) ? mf : 50,
|
||||||
|
FbTimezone = txtFbTimezone?.Text?.Trim() ?? "Europe/Rome",
|
||||||
|
FbLeagueIds = ParseIntList(txtFbLeagueIds?.Text),
|
||||||
|
FbCheckQuota = chkFbCheckQuota.IsChecked == true,
|
||||||
|
FbMinRemainingQuota = int.TryParse(txtFbMinQuota.Text.Trim(), out var mq) ? mq : 10,
|
||||||
|
FbApiDelayMs = int.TryParse(txtFbApiDelay.Text.Trim(), out var ad) ? ad : 300,
|
||||||
|
// Racing
|
||||||
RcExportPath = txtRcExportPath.Text.Trim(),
|
RcExportPath = txtRcExportPath.Text.Trim(),
|
||||||
RcPrefix = txtRcPrefix.Text.Trim(),
|
RcPrefix = txtRcPrefix.Text.Trim(),
|
||||||
RcSuffix = txtRcSuffix.Text.Trim(),
|
RcSuffix = txtRcSuffix.Text.Trim(),
|
||||||
@@ -1126,6 +1178,50 @@ namespace HorseRacingPredictor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ???????????? FOOTBALL DOWNLOAD OPTIONS HELPERS ????????????
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Costruisce un FootballDownloadOptions leggendo i valori dalla UI.
|
||||||
|
/// </summary>
|
||||||
|
private Football.FootballDownloadOptions BuildFootballDownloadOptions()
|
||||||
|
{
|
||||||
|
return new Football.FootballDownloadOptions
|
||||||
|
{
|
||||||
|
DownloadFixtures = chkFbFixtures.IsChecked == true,
|
||||||
|
DownloadOdds = chkFbOdds.IsChecked == true,
|
||||||
|
DownloadPredictions = chkFbPredictions.IsChecked == true,
|
||||||
|
DownloadStandings = chkFbStandings.IsChecked == true,
|
||||||
|
DownloadH2H = chkFbH2H.IsChecked == true,
|
||||||
|
DownloadEvents = chkFbEvents.IsChecked == true,
|
||||||
|
DownloadLineups = chkFbLineups.IsChecked == true,
|
||||||
|
DownloadStatistics = chkFbStatistics.IsChecked == true,
|
||||||
|
DownloadInjuries = chkFbInjuries.IsChecked == true,
|
||||||
|
BookmakerId = int.TryParse(txtFbBookmakerId.Text.Trim(), out var bId) ? bId : 8,
|
||||||
|
OddsMaxPages = int.TryParse(txtFbOddsMaxPages.Text.Trim(), out var omp) ? omp : 3,
|
||||||
|
MaxFixturesForDetails = int.TryParse(txtFbMaxFixtures.Text.Trim(), out var mf) ? mf : 50,
|
||||||
|
Timezone = txtFbTimezone?.Text?.Trim() ?? "Europe/Rome",
|
||||||
|
LeagueIds = ParseIntList(txtFbLeagueIds?.Text),
|
||||||
|
CheckQuota = chkFbCheckQuota.IsChecked == true,
|
||||||
|
MinRemainingQuota = int.TryParse(txtFbMinQuota.Text.Trim(), out var mq) ? mq : 10,
|
||||||
|
ApiDelayMs = int.TryParse(txtFbApiDelay.Text.Trim(), out var ad) ? ad : 300
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analizza una stringa di interi separati da virgola e restituisce una lista.
|
||||||
|
/// </summary>
|
||||||
|
private static List<int> ParseIntList(string text)
|
||||||
|
{
|
||||||
|
var result = new List<int>();
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return result;
|
||||||
|
foreach (var part in text.Split(',', ';', ' '))
|
||||||
|
{
|
||||||
|
if (int.TryParse(part.Trim(), out var val))
|
||||||
|
result.Add(val);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// ???????????????????????? VIRTUAL FOOTBALL ????????????????????????
|
// ???????????????????????? VIRTUAL FOOTBALL ????????????????????????
|
||||||
|
|
||||||
private void btnVfbNavigate_Click(object sender, RoutedEventArgs e)
|
private void btnVfbNavigate_Click(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
Reference in New Issue
Block a user