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
+28
View File
@@ -0,0 +1,28 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35707.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BettingPredictor", "HorseRacingPredictor\BettingPredictor.csproj", "{63138155-B7F3-4246-B47B-B8CC2D7A60A4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Debug|x64.ActiveCfg = Debug|x64
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Debug|x64.Build.0 = Debug|x64
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Release|Any CPU.Build.0 = Release|Any CPU
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Release|x64.ActiveCfg = Release|x64
{63138155-B7F3-4246-B47B-B8CC2D7A60A4}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- System.Threading.Tasks.Extensions -->
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly>
<!-- System.Memory -->
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>
<!-- System.Runtime.CompilerServices.Unsafe -->
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly>
<!-- System.Buffers -->
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>
<!-- System.Numerics.Vectors -->
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.6.0" newVersion="4.1.6.0" />
</dependentAssembly>
<!-- Microsoft.Bcl.AsyncInterfaces -->
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.HashCode" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Tensors" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
@@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.FastTree.props" Condition="Exists('..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.FastTree.props')" />
<Import Project="..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.props" Condition="Exists('..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.props')" />
<Import Project="..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.CpuMath.props" Condition="Exists('..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.CpuMath.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{63138155-B7F3-4246-B47B-B8CC2D7A60A4}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>HorseRacingPredictor</RootNamespace>
<AssemblyName>HorseRacingPredictor</AssemblyName>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="CsvHelper, Version=33.0.0.0, Culture=neutral, PublicKeyToken=8c4959082be5c823, processorArchitecture=MSIL">
<HintPath>..\packages\CsvHelper.33.0.1\lib\net48\CsvHelper.dll</HintPath>
</Reference>
<Reference Include="Dapper, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.2.1.66\lib\net461\Dapper.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.0-preview.4.25258.110\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.HashCode, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.HashCode.6.0.0\lib\net462\Microsoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.Numerics, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.Numerics.10.0.0-preview.4.25258.110\lib\net462\Microsoft.Bcl.Numerics.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.CpuMath, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.CpuMath.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Data.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.DataView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.DataView.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.DataView.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.FastTree, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.FastTree.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.KMeansClustering, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.KMeansClustering.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.PCA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.PCA.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.StandardTrainers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.StandardTrainers.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Transforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Transforms.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=112.1.1.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
<HintPath>..\packages\RestSharp.112.1.1-alpha.0.4\lib\net48\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.CodeDom, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.CodeDom.10.0.0-preview.4.25258.110\lib\net462\System.CodeDom.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.10.0.0-preview.4.25258.110\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.IO.Pipelines, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Pipelines.10.0.0-preview.2.25163.2\lib\net462\System.IO.Pipelines.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Tensors, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Tensors.10.0.0-preview.4.25258.110\lib\net462\System.Numerics.Tensors.dll</HintPath>
</Reference>
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encodings.Web, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encodings.Web.10.0.0-preview.2.25163.2\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference>
<Reference Include="System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Json.10.0.0-preview.2.25163.2\lib\net462\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Channels, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Channels.10.0.0-preview.4.25258.110\lib\net462\System.Threading.Channels.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Football\Database\BetType.cs" />
<Compile Include="Football\Database\Bookmaker.cs" />
<Compile Include="Football\Database\APIResponse.cs" />
<Compile Include="Football\Database\TeamStats.cs" />
<Compile Include="Football\Database\LeagueStats.cs" />
<Compile Include="Football\Database\H2H.cs" />
<Compile Include="Football\Database\Comparison.cs" />
<Compile Include="Football\Database\FixtureLeague.cs" />
<Compile Include="Football\Main.cs" />
<Compile Include="Football\Manager\API.cs" />
<Compile Include="Football\API\Fixture.cs" />
<Compile Include="Football\API\League.cs" />
<Compile Include="Football\API\Odds.cs" />
<Compile Include="Football\API\Prediction.cs" />
<Compile Include="Football\Database\Fixture.cs" />
<Compile Include="Football\Database\Goals.cs" />
<Compile Include="Football\Database\League.cs" />
<Compile Include="Football\Database\Odds.cs" />
<Compile Include="Football\Database\Prediction.cs" />
<Compile Include="Football\Database\Score.cs" />
<Compile Include="Football\Database\Team.cs" />
<Compile Include="Horses\Calculator.cs" />
<Compile Include="Horses\Files\Maps\Punters.cs" />
<Compile Include="Horses\ML\HorseRacePrediction.cs" />
<Compile Include="Horses\ML\MachineLearningService.cs" />
<Compile Include="Horses\ML\ModelInput.cs" />
<Compile Include="Horses\ML\ModelOutput.cs" />
<Compile Include="Manager\API.cs" />
<Compile Include="Manager\FileReader.cs" />
<Compile Include="Manager\Database.cs" />
<Compile Include="Football\Manager\Database.cs" />
<Compile Include="Horses\FileReader.cs" />
<Compile Include="Horses\Database.cs" />
<Compile Include="Horses\Files\Punters.cs" />
<Compile Include="Main.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Main.Designer.cs">
<DependentUpon>Main.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Main.resx">
<DependentUpon>Main.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Dati\Result.csv" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Query\ScanFavoriteHorseWeights.sql" />
<Content Include="Query\PredictionScanResults.sql" />
<Content Include="Query\GetFavoriteHorses.sql" />
<Content Include="Query\GetValidHorses.sql" />
<Content Include="Query\Races.sql" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Questo progetto fa riferimento a uno o più pacchetti NuGet che non sono presenti in questo computer. Usare lo strumento di ripristino dei pacchetti NuGet per scaricarli. Per altre informazioni, vedere http://go.microsoft.com/fwlink/?LinkID=322105. Il file mancante è {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.CpuMath.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.CpuMath.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.FastTree.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.FastTree.props'))" />
</Target>
<Import Project="..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.targets" Condition="Exists('..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\build\netstandard2.0\Microsoft.ML.targets')" />
</Project>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,59 @@
using System;
using System.Threading.Tasks;
using HorseRacingPredictor.Football.Manager;
using RestSharp;
namespace HorseRacingPredictor.Football.API
{
internal class Fixture : HorseRacingPredictor.Football.Manager.API
{
private const string Endpoint = "fixtures";
/// <summary>
/// Ottiene le partite per una data specifica
/// </summary>
public new RestResponse GetFixturesByDate(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 delle partite per la data {date.ToShortDateString()}: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di GetFixturesByDate
/// </summary>
public new async Task<RestResponse> GetFixturesByDateAsync(DateTime date)
{
try
{
string dateStr = date.ToString("yyyy-MM-dd");
return await ExecuteRequestAsync($"{Endpoint}?date={dateStr}");
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero asincrono delle partite per la data {date.ToShortDateString()}: {ex.Message}", ex);
}
}
/// <summary>
/// Ottiene le partite per un ID specifico
/// </summary>
public new RestResponse GetFixtureById(int fixtureId)
{
try
{
return ExecuteRequest($"{Endpoint}?id={fixtureId}", ApiTypes.Fixtures);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero della partita con ID {fixtureId}: {ex.Message}", ex);
}
}
}
}
@@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using RestSharp;
namespace HorseRacingPredictor.Football.API
{
internal class League : HorseRacingPredictor.Football.Manager.API
{
private const string Endpoint = "leagues";
/// <summary>
/// Ottiene informazioni sui campionati
/// </summary>
public new RestResponse GetLeagues()
{
try
{
return ExecuteRequest(Endpoint, ApiTypes.Leagues);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero delle leghe: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di GetLeagues
/// </summary>
public new async Task<RestResponse> GetLeaguesAsync()
{
try
{
return await ExecuteRequestAsync(Endpoint);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero asincrono delle leghe: {ex.Message}", ex);
}
}
/// <summary>
/// Ottiene informazioni su una specifica stagione di una lega
/// </summary>
public new RestResponse GetLeagueBySeason(int leagueId, int season)
{
try
{
return ExecuteRequest($"{Endpoint}?id={leagueId}&season={season}", ApiTypes.Leagues);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero della lega {leagueId} per la stagione {season}: {ex.Message}", ex);
}
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Threading.Tasks;
using RestSharp;
namespace HorseRacingPredictor.Football.API
{
internal class Odds : HorseRacingPredictor.Football.Manager.API
{
private const string Endpoint = "odds";
/// <summary>
/// Ottiene le quote per una data specifica
/// </summary>
public RestResponse GetOddsByDate(DateTime date, int bookmaker = 8, int page = 1)
{
try
{
string dateStr = date.ToString("yyyy-MM-dd");
return ExecuteRequest($"{Endpoint}?date={dateStr}&bookmaker={bookmaker}&page={page}", ApiTypes.Odds);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero delle quote per la data {date.ToShortDateString()}: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di GetOddsByDate
/// </summary>
public async Task<RestResponse> GetOddsByDateAsync(DateTime date, int bookmaker = 8, int page = 1)
{
try
{
string dateStr = date.ToString("yyyy-MM-dd");
return await ExecuteRequestAsync($"{Endpoint}?date={dateStr}&bookmaker={bookmaker}&page={page}");
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero asincrono delle quote per la data {date.ToShortDateString()}: {ex.Message}", ex);
}
}
/// <summary>
/// Ottiene le quote per una partita specifica
/// </summary>
public RestResponse GetOddsByFixture(int fixtureId, int bookmaker = 8)
{
try
{
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}&bookmaker={bookmaker}", ApiTypes.Odds);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero delle quote per la partita {fixtureId}: {ex.Message}", ex);
}
}
}
}
@@ -0,0 +1,41 @@
using System;
using System.Threading.Tasks;
using RestSharp;
namespace HorseRacingPredictor.Football.API
{
internal class Prediction : HorseRacingPredictor.Football.Manager.API
{
private const string Endpoint = "predictions";
/// <summary>
/// Ottiene le previsioni per una partita specifica
/// </summary>
public RestResponse GetPredictionByFixture(int fixtureId)
{
try
{
return ExecuteRequest($"{Endpoint}?fixture={fixtureId}", ApiTypes.Predictions);
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero delle previsioni per la partita {fixtureId}: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di GetPredictionByFixture
/// </summary>
public async Task<RestResponse> GetPredictionByFixtureAsync(int fixtureId)
{
try
{
return await ExecuteRequestAsync($"{Endpoint}?fixture={fixtureId}");
}
catch (Exception ex)
{
throw new Exception($"Errore durante il recupero asincrono delle previsioni per la partita {fixtureId}: {ex.Message}", ex);
}
}
}
}
@@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using RestSharp;
namespace HorseRacingPredictor.Football.Database
{
/// <summary>
/// Classe per la gestione della tabella API_Response che memorizza le risposte API prima dell'elaborazione
/// </summary>
internal class APIResponse : HorseRacingPredictor.Football.Manager.Database
{
/// <summary>
/// Inserisce una nuova risposta API nel database
/// </summary>
/// <param name="apiType">Tipo di API (es. FootballFixtures, FootballOdds)</param>
/// <param name="endpoint">Endpoint API chiamato</param>
/// <param name="parameters">Parametri utilizzati nella chiamata</param>
/// <param name="response">Risposta API (RestResponse)</param>
/// <returns>ID della risposta inserita</returns>
public int InsertApiResponse(string apiType, string endpoint, string parameters, RestResponse response)
{
int responseId = 0;
try
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
string query = @"
INSERT INTO API_Response (
timestamp, api_type, endpoint, parameters,
response_content, status_code, processed, processed_timestamp, error_message
) VALUES (
@timestamp, @apiType, @endpoint, @parameters,
@responseContent, @statusCode, @processed, NULL, @errorMessage
);
SELECT SCOPE_IDENTITY();";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@timestamp", DateTime.Now);
command.Parameters.AddWithValue("@apiType", apiType);
command.Parameters.AddWithValue("@endpoint", endpoint);
command.Parameters.AddWithValue("@parameters", parameters ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@responseContent", response.Content);
command.Parameters.AddWithValue("@statusCode", (int)response.StatusCode);
command.Parameters.AddWithValue("@processed", false);
command.Parameters.AddWithValue("@errorMessage",
response.IsSuccessful ? DBNull.Value : (object)(response.ErrorMessage ?? "Unknown error"));
var result = command.ExecuteScalar();
if (result != null && result != DBNull.Value)
{
responseId = Convert.ToInt32(result);
}
}
}
}
catch (Exception ex)
{
LogError("inserimento risposta API", ex);
throw;
}
return responseId;
}
/// <summary>
/// Aggiorna lo stato di elaborazione di una risposta API
/// </summary>
/// <param name="responseId">ID della risposta API</param>
/// <param name="processed">Stato di elaborazione</param>
/// <param name="errorMessage">Messaggio di errore (se presente)</param>
public void UpdateProcessingStatus(int responseId, bool processed, string errorMessage = null)
{
try
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
string query = @"
UPDATE API_Response
SET processed = @processed,
processed_timestamp = @processedTimestamp,
error_message = @errorMessage
WHERE id = @id";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@processed", processed);
command.Parameters.AddWithValue("@processedTimestamp", processed ? DateTime.Now : (object)DBNull.Value);
command.Parameters.AddWithValue("@errorMessage", errorMessage ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@id", responseId);
command.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
LogError($"aggiornamento stato elaborazione risposta API ID:{responseId}", ex);
throw;
}
}
/// <summary>
/// Ottiene le risposte API non elaborate per un determinato tipo
/// </summary>
/// <param name="apiType">Tipo di API (opzionale)</param>
/// <returns>DataTable con le risposte API non elaborate</returns>
public DataTable GetUnprocessedResponses(string apiType = null)
{
DataTable dataTable = new DataTable();
try
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
StringBuilder queryBuilder = new StringBuilder(@"
SELECT id, timestamp, api_type, endpoint, parameters,
response_content, status_code, processed
FROM API_Response
WHERE processed = 0 AND status_code = 200");
if (!string.IsNullOrEmpty(apiType))
{
queryBuilder.Append(" AND api_type = @apiType");
}
queryBuilder.Append(" ORDER BY timestamp ASC");
using (var command = new SqlCommand(queryBuilder.ToString(), connection))
{
if (!string.IsNullOrEmpty(apiType))
{
command.Parameters.AddWithValue("@apiType", apiType);
}
using (var adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataTable);
}
}
}
}
catch (Exception ex)
{
LogError("recupero risposte API non elaborate", ex);
throw;
}
return dataTable;
}
/// <summary>
/// Ottiene una specifica risposta API dal database
/// </summary>
/// <param name="responseId">ID della risposta API</param>
/// <returns>DataRow con i dati della risposta API o null se non trovata</returns>
public DataRow GetApiResponseById(int responseId)
{
DataTable dataTable = new DataTable();
try
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
string query = @"
SELECT id, timestamp, api_type, endpoint, parameters,
response_content, status_code, processed
FROM API_Response
WHERE id = @id";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@id", responseId);
using (var adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataTable);
}
}
}
if (dataTable.Rows.Count > 0)
{
return dataTable.Rows[0];
}
}
catch (Exception ex)
{
LogError($"recupero risposta API ID:{responseId}", ex);
throw;
}
return null;
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class BetType : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode betType)
{
try
{
var id = betType["id"]?.GetValue<int>();
if (id == null) return; // Salta il record se l'id è null
var query = @"
IF EXISTS (SELECT 1 FROM BetType WHERE bet_type_id = @bet_type_id)
BEGIN
UPDATE BetType SET name = @name WHERE bet_type_id = @bet_type_id
END
ELSE
BEGIN
INSERT INTO BetType (bet_type_id, name) VALUES (@bet_type_id, @name)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@bet_type_id", id);
command.Parameters.AddWithValue("@name", betType["name"]?.GetValue<string>() ?? "");
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert del tipo di scommessa", ex);
}
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class Bookmaker : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode bookmaker)
{
try
{
var id = bookmaker["id"]?.GetValue<int>();
if (id == null) return; // Salta il record se l'id è null
var query = @"
IF EXISTS (SELECT 1 FROM Bookmaker WHERE bookmaker_id = @bookmaker_id)
BEGIN
UPDATE Bookmaker SET name = @name WHERE bookmaker_id = @bookmaker_id
END
ELSE
BEGIN
INSERT INTO Bookmaker (bookmaker_id, name) VALUES (@bookmaker_id, @name)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@bookmaker_id", id);
command.Parameters.AddWithValue("@name", bookmaker["name"]?.GetValue<string>() ?? "");
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert del bookmaker", ex);
}
}
}
}
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class Comparison : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, int predictionId, JsonNode comparison)
{
try
{
if (comparison == null) return;
var query = @"
IF EXISTS (SELECT 1 FROM Comparison WHERE prediction_id = @prediction_id)
BEGIN
UPDATE Comparison
SET form_home = @form_home,
form_away = @form_away,
att_home = @att_home,
att_away = @att_away,
def_home = @def_home,
def_away = @def_away,
poisson_home = @poisson_home,
poisson_away = @poisson_away,
h2h_home = @h2h_home,
h2h_away = @h2h_away,
goals_home = @goals_home,
goals_away = @goals_away,
total_home = @total_home,
total_away = @total_away
WHERE prediction_id = @prediction_id
END
ELSE
BEGIN
INSERT INTO Comparison (
prediction_id, form_home, form_away, att_home, att_away,
def_home, def_away, poisson_home, poisson_away,
h2h_home, h2h_away, goals_home, goals_away,
total_home, total_away
) VALUES (
@prediction_id, @form_home, @form_away, @att_home, @att_away,
@def_home, @def_away, @poisson_home, @poisson_away,
@h2h_home, @h2h_away, @goals_home, @goals_away,
@total_home, @total_away
)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@prediction_id", predictionId);
// Aggiungi tutti i parametri del confronto
command.Parameters.AddWithValue("@form_home", comparison["form"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@form_away", comparison["form"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@att_home", comparison["att"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@att_away", comparison["att"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@def_home", comparison["def"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@def_away", comparison["def"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@poisson_home", comparison["poisson_distribution"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@poisson_away", comparison["poisson_distribution"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@h2h_home", comparison["h2h"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@h2h_away", comparison["h2h"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@goals_home", comparison["goals"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@goals_away", comparison["goals"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@total_home", comparison["total"]?["home"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@total_away", comparison["total"]?["away"]?.GetValue<string>() ?? (object)DBNull.Value);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert del confronto di previsione", ex);
}
}
}
}
@@ -0,0 +1,203 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Fixture : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, SqlTransaction transaction, JsonNode fixture)
{
try
{
var id = fixture["id"]?.GetValue<int>();
if (id == null) return; // Salta il record se l'id è null
var query = @"
IF EXISTS (SELECT 1 FROM Fixture WHERE fixture_id = @fixture_id)
BEGIN
UPDATE Fixture SET
referee = @referee,
timezone = @timezone,
date = @date,
timestamp = @timestamp,
venue_id = @venue_id,
status_long = @status_long,
status_short = @status_short,
status_elapsed = @status_elapsed,
status_extra = @status_extra
WHERE fixture_id = @fixture_id
END
ELSE
BEGIN
INSERT INTO Fixture (
fixture_id,
referee,
timezone,
date,
timestamp,
venue_id,
status_long,
status_short,
status_elapsed,
status_extra
) VALUES (
@fixture_id,
@referee,
@timezone,
@date,
@timestamp,
@venue_id,
@status_long,
@status_short,
@status_elapsed,
@status_extra
)
END";
using (var command = new SqlCommand(query, connection, transaction))
{
// Estrai tutti i valori dal JSON con gestione dei valori null
var status = fixture["status"];
var venue = fixture["venue"];
// Parametri principali
command.Parameters.AddWithValue("@fixture_id", id);
command.Parameters.AddWithValue("@timezone", fixture["timezone"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@date", fixture["date"]?.GetValue<DateTime>() ?? DateTime.MinValue);
command.Parameters.AddWithValue("@timestamp", fixture["timestamp"]?.GetValue<long>() ?? 0L);
command.Parameters.AddWithValue("@referee", fixture["referee"]?.GetValue<string>() ?? (object)DBNull.Value);
// Parametri venue
int? venueId = venue?["id"]?.GetValue<int?>();
command.Parameters.AddWithValue("@venue_id", venueId.HasValue ? (object)venueId.Value : DBNull.Value);
// Parametri status
command.Parameters.AddWithValue("@status_long", status?["long"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@status_short", status?["short"]?.GetValue<string>() ?? "");
int? elapsed = status?["elapsed"]?.GetValue<int?>();
int? extra = null; // Assumo che l'API restituisca questo valore, altrimenti va gestito come gli altri
command.Parameters.AddWithValue("@status_elapsed", elapsed.HasValue ? (object)elapsed.Value : DBNull.Value);
command.Parameters.AddWithValue("@status_extra", extra.HasValue ? (object)extra.Value : DBNull.Value);
command.ExecuteNonQuery();
}
// Gestione della venue, se necessario
if (fixture["venue"] != null && fixture["venue"]["id"] != null)
{
UpsertVenue(connection, transaction, fixture["venue"]);
}
}
catch (Exception ex)
{
LogError("l'upsert del fixture", ex);
throw; // Rilancia l'eccezione per il rollback della transazione
}
}
/// <summary>
/// Metodo di supporto per inserire/aggiornare le informazioni sulla venue
/// </summary>
private void UpsertVenue(SqlConnection connection, SqlTransaction transaction, JsonNode venue)
{
try
{
var id = venue["id"]?.GetValue<int>();
if (id == null) return;
var query = @"
IF EXISTS (SELECT 1 FROM Venue WHERE venue_id = @venue_id)
BEGIN
UPDATE Venue SET name = @name, city = @city WHERE venue_id = @venue_id
END
ELSE
BEGIN
INSERT INTO Venue (venue_id, name, city) VALUES (@venue_id, @name, @city)
END";
using (var command = new SqlCommand(query, connection, transaction))
{
command.Parameters.AddWithValue("@venue_id", id);
command.Parameters.AddWithValue("@name", venue["name"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@city", venue["city"]?.GetValue<string>() ?? "");
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert della venue", ex);
throw; // Rilancia l'eccezione per il rollback della transazione
}
}
/// <summary>
/// Recupera i fixture elaborati dal database con tutte le informazioni correlate
/// </summary>
/// <returns>DataTable contenente tutte le partite con le relative quote e previsioni</returns>
public DataTable GetProcessedFixtures()
{
var result = new DataTable();
try
{
using (var connection = new System.Data.SqlClient.SqlConnection(_connectionString))
{
connection.Open();
string query = @"
SELECT
f.fixture_id AS ID,
l.country AS Paese,
l.name AS Campionato,
f.date AS [Data / Ora],
f.status AS Stato,
th.name AS Casa,
ta.name AS Trasferta,
g.home AS [Goals Casa],
g.away AS [Goals Trasferta],
o1.value AS [Quota Casa],
o2.value AS [Quota Pareggio],
o3.value AS [Quota Trasferta],
p.winner_name AS Previsione
FROM
Fixture f
JOIN FixtureLeague fl ON f.fixture_id = fl.fixture_id
JOIN League l ON fl.league_id = l.league_id
JOIN Team th ON th.team_id = f.home_team_id
JOIN Team ta ON ta.team_id = f.away_team_id
LEFT JOIN Goals g ON f.fixture_id = g.fixture_id
LEFT JOIN (
SELECT fixture_id, value FROM Odds
WHERE bet_id = 1 AND value_name = 'Home' AND bookmaker_id = 8
) o1 ON f.fixture_id = o1.fixture_id
LEFT JOIN (
SELECT fixture_id, value FROM Odds
WHERE bet_id = 1 AND value_name = 'Draw' AND bookmaker_id = 8
) o2 ON f.fixture_id = o2.fixture_id
LEFT JOIN (
SELECT fixture_id, value FROM Odds
WHERE bet_id = 1 AND value_name = 'Away' AND bookmaker_id = 8
) o3 ON f.fixture_id = o3.fixture_id
LEFT JOIN Prediction p ON f.fixture_id = p.fixture_id
ORDER BY
f.date ASC";
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(query, connection))
{
adapter.Fill(result);
}
}
}
catch (Exception ex)
{
LogError("recupero partite elaborate dal database", ex);
// Return empty table in case of error
}
return result;
}
}
}
@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class FixtureLeague : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, SqlTransaction transaction, int fixtureId, int leagueId, JsonNode leagueData)
{
try
{
// Estrai i valori prima della query per utilizzarli nel log
string round = leagueData["round"]?.GetValue<string>() ?? "";
bool? standings = leagueData["standings"]?.GetValue<bool?>();
string standingsValue = standings.HasValue ? standings.Value.ToString() : "NULL";
// Log dell'operazione prima di tentare l'upsert
LogDebugInfo($"Tentativo di upsert FixtureLeague: fixture_id={fixtureId}, league_id={leagueId}, round='{round}', standings={standingsValue}");
var query = @"
IF EXISTS (SELECT 1 FROM FixtureLeague WHERE fixture_id = @fixture_id AND league_id = @league_id)
BEGIN
UPDATE FixtureLeague
SET round = @round, standings = @standings
WHERE fixture_id = @fixture_id AND league_id = @league_id
END
ELSE
BEGIN
INSERT INTO FixtureLeague (fixture_id, league_id, round, standings)
VALUES (@fixture_id, @league_id, @round, @standings)
END";
using (var command = new SqlCommand(query, connection, transaction))
{
command.Parameters.AddWithValue("@fixture_id", fixtureId);
command.Parameters.AddWithValue("@league_id", leagueId);
command.Parameters.AddWithValue("@round", round);
command.Parameters.AddWithValue("@standings", standings.HasValue ? (object)standings.Value : DBNull.Value);
command.ExecuteNonQuery();
}
// Log di successo dopo l'operazione
LogDebugInfo($"Upsert FixtureLeague completato con successo: fixture_id={fixtureId}, league_id={leagueId}");
}
catch (Exception ex)
{
// Log dettagliato dell'errore
StringBuilder detailedError = new StringBuilder();
detailedError.AppendLine($"Errore durante l'upsert della relazione fixture-league:");
detailedError.AppendLine($"- fixture_id: {fixtureId}");
detailedError.AppendLine($"- league_id: {leagueId}");
detailedError.AppendLine($"- round: '{leagueData["round"]?.ToString() ?? "NULL"}'");
detailedError.AppendLine($"- standings: {leagueData["standings"]?.ToString() ?? "NULL"}");
detailedError.AppendLine($"- leagueData: {leagueData?.ToString() ?? "NULL"}");
detailedError.AppendLine($"- Messaggio errore: {ex.Message}");
if (ex.InnerException != null)
{
detailedError.AppendLine($"- Inner exception: {ex.InnerException.Message}");
}
// Controlla se la fixture esiste
CheckIfFixtureExists(connection, transaction, fixtureId, detailedError);
// Controlla se la league esiste
CheckIfLeagueExists(connection, transaction, leagueId, detailedError);
LogError("l'upsert della relazione fixture-league", new Exception(detailedError.ToString(), ex));
throw; // Rilancia l'eccezione per il rollback della transazione
}
}
/// <summary>
/// Verifica se una fixture esiste nel database e aggiunge l'informazione al log
/// </summary>
private void CheckIfFixtureExists(SqlConnection connection, SqlTransaction transaction, int fixtureId, StringBuilder logBuilder)
{
try
{
using (var cmd = new SqlCommand("SELECT COUNT(1) FROM Fixture WHERE fixture_id = @id", connection, transaction))
{
cmd.Parameters.AddWithValue("@id", fixtureId);
int count = Convert.ToInt32(cmd.ExecuteScalar());
logBuilder.AppendLine($"- Fixture con ID {fixtureId} {(count > 0 ? "esiste" : "NON esiste")} nel database");
}
}
catch (Exception ex)
{
logBuilder.AppendLine($"- Errore durante la verifica dell'esistenza della fixture: {ex.Message}");
}
}
/// <summary>
/// Verifica se una league esiste nel database e aggiunge l'informazione al log
/// </summary>
private void CheckIfLeagueExists(SqlConnection connection, SqlTransaction transaction, int leagueId, StringBuilder logBuilder)
{
try
{
using (var cmd = new SqlCommand("SELECT COUNT(1) FROM League WHERE league_id = @id", connection, transaction))
{
cmd.Parameters.AddWithValue("@id", leagueId);
int count = Convert.ToInt32(cmd.ExecuteScalar());
logBuilder.AppendLine($"- League con ID {leagueId} {(count > 0 ? "esiste" : "NON esiste")} nel database");
}
}
catch (Exception ex)
{
logBuilder.AppendLine($"- Errore durante la verifica dell'esistenza della league: {ex.Message}");
}
}
/// <summary>
/// Logga informazioni di debug (utile per tracciare il flusso di esecuzione)
/// </summary>
private void LogDebugInfo(string message)
{
try
{
// Utilizziamo LogError con exception null come metodo per loggare informazioni di debug
LogError("DEBUG INFO", new Exception(message));
}
catch
{
// Ignoriamo eventuali errori nel logging di debug
}
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Goals : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode goals, int fixtureId)
{
try
{
// Utilizza la tabella Score invece di una tabella Goals che non esiste
var query = @"
IF EXISTS (SELECT 1 FROM Score WHERE fixture_id = @fixture_id)
BEGIN
UPDATE Score SET home_goals = @home, away_goals = @away WHERE fixture_id = @fixture_id
END
ELSE
BEGIN
INSERT INTO Score (home_goals, away_goals, fixture_id,
home_halftime, away_halftime,
home_fulltime, away_fulltime,
home_extratime, away_extratime,
home_penalty, away_penalty)
VALUES (@home, @away, @fixture_id, 0, 0, 0, 0, 0, 0, 0, 0)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@home", goals["home"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@away", goals["away"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@fixture_id", fixtureId);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert dei goal", ex);
}
}
}
}
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class H2H : HorseRacingPredictor.Football.Manager.Database
{
public void Insert(SqlConnection connection, int predictionId, int h2hFixtureId)
{
try
{
var query = @"
IF NOT EXISTS (SELECT 1 FROM H2H WHERE prediction_id = @prediction_id AND fixture_id = @fixture_id)
BEGIN
INSERT INTO H2H (prediction_id, fixture_id)
VALUES (@prediction_id, @fixture_id)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@prediction_id", predictionId);
command.Parameters.AddWithValue("@fixture_id", h2hFixtureId);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'inserimento della relazione H2H", ex);
}
}
public void DeleteForPrediction(SqlConnection connection, int predictionId)
{
try
{
var query = "DELETE FROM H2H WHERE prediction_id = @prediction_id";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@prediction_id", predictionId);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'eliminazione delle relazioni H2H", ex);
}
}
}
}
@@ -0,0 +1,76 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class League : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode league)
{
try
{
var id = league["id"]?.GetValue<int>();
if (id == null) return; // Salta il record se l'id è null
var query = @"
IF EXISTS (SELECT 1 FROM League WHERE league_id = @league_id)
BEGIN
UPDATE League SET name = @name, country = @country, logo = @logo, flag = @flag, season = @season WHERE league_id = @league_id
END
ELSE
BEGIN
INSERT INTO League (league_id, name, country, logo, flag, season) VALUES (@league_id, @name, @country, @logo, @flag, @season)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@league_id", id);
command.Parameters.AddWithValue("@name", league["name"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@country", league["country"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@logo", league["logo"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@flag", league["flag"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@season", league["season"]?.GetValue<int>() ?? 0);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert della lega", ex);
}
}
public void Upsert(SqlConnection connection, SqlTransaction transaction, JsonNode league)
{
try
{
var id = league["id"]?.GetValue<int>();
if (id == null) return; // Salta il record se l'id è null
var query = @"
IF EXISTS (SELECT 1 FROM League WHERE league_id = @league_id)
BEGIN
UPDATE League SET name = @name, country = @country, logo = @logo, flag = @flag, season = @season WHERE league_id = @league_id
END
ELSE
BEGIN
INSERT INTO League (league_id, name, country, logo, flag, season) VALUES (@league_id, @name, @country, @logo, @flag, @season)
END";
using (var command = new SqlCommand(query, connection, transaction))
{
command.Parameters.AddWithValue("@league_id", id);
command.Parameters.AddWithValue("@name", league["name"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@country", league["country"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@logo", league["logo"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@flag", league["flag"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@season", league["season"]?.GetValue<int>() ?? 0);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert della lega", ex);
throw; // Rilancia l'eccezione per il rollback della transazione
}
}
}
}
@@ -0,0 +1,89 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class LeagueStats : HorseRacingPredictor.Football.Manager.Database
{
public int Insert(SqlConnection connection, int? teamId, int? predictionId, bool isHome, JsonNode stats)
{
try
{
// Ottieni il prossimo ID di statistiche
int statsId = GetNextStatsId(connection);
if (statsId <= 0) return 0;
var query = @"
INSERT INTO LeagueStats (
stats_id, team_id, prediction_id, is_home, form,
fixtures_played_home, fixtures_played_away, fixtures_played_total,
wins_home, wins_away, wins_total,
draws_home, draws_away, draws_total,
loses_home, loses_away, loses_total
) VALUES (
@stats_id, @team_id, @prediction_id, @is_home, @form,
@fixtures_played_home, @fixtures_played_away, @fixtures_played_total,
@wins_home, @wins_away, @wins_total,
@draws_home, @draws_away, @draws_total,
@loses_home, @loses_away, @loses_total
)";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@stats_id", statsId);
command.Parameters.AddWithValue("@team_id", teamId.HasValue ? (object)teamId.Value : DBNull.Value);
command.Parameters.AddWithValue("@prediction_id", predictionId.HasValue ? (object)predictionId.Value : DBNull.Value);
command.Parameters.AddWithValue("@is_home", isHome);
command.Parameters.AddWithValue("@form", stats["form"]?.GetValue<string>() ?? (object)DBNull.Value);
var fixtures = stats["fixtures"];
command.Parameters.AddWithValue("@fixtures_played_home", fixtures?["played"]?["home"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@fixtures_played_away", fixtures?["played"]?["away"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@fixtures_played_total", fixtures?["played"]?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
var wins = stats["fixtures"]?["wins"];
command.Parameters.AddWithValue("@wins_home", wins?["home"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@wins_away", wins?["away"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@wins_total", wins?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
var draws = stats["fixtures"]?["draws"];
command.Parameters.AddWithValue("@draws_home", draws?["home"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@draws_away", draws?["away"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@draws_total", draws?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
var loses = stats["fixtures"]?["loses"];
command.Parameters.AddWithValue("@loses_home", loses?["home"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@loses_away", loses?["away"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@loses_total", loses?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.ExecuteNonQuery();
}
return statsId;
}
catch (Exception ex)
{
LogError("l'inserimento delle statistiche di lega", ex);
return 0;
}
}
private int GetNextStatsId(SqlConnection connection)
{
try
{
var query = "SELECT ISNULL(MAX(stats_id), 0) + 1 FROM LeagueStats";
using (var command = new SqlCommand(query, connection))
{
return Convert.ToInt32(command.ExecuteScalar());
}
}
catch (Exception ex)
{
LogError("il recupero del prossimo ID statistiche", ex);
return 0;
}
}
}
}
@@ -0,0 +1,198 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Odds : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode bookmakers, int fixtureId)
{
try
{
// Aggiungiamo log per tracciare la struttura dei dati
LogError($"Inizio elaborazione quote per fixture {fixtureId} con {bookmakers?.AsArray().Count ?? 0} bookmakers", null);
// Per ogni bookmaker
foreach (var bookmaker in bookmakers.AsArray())
{
int bookmakerId = bookmaker["id"].GetValue<int>();
var bets = bookmaker["bets"];
// Cerchiamo le quote di tipo Match Winner (1X2)
foreach (var bet in bets.AsArray())
{
// Gestiamo in modo più sicuro il recupero di valori dal JSON
string betName = GetStringValueSafe(bet, "name");
// Log per debug
LogError($"Elaborazione bet: '{betName}' per bookmaker {bookmakerId}", null);
// Determina il tipo di scommessa (assumiamo che esista una tabella BetType)
int betTypeId = GetBetTypeId(connection, betName);
// Se non riusciamo a determinare il tipo di scommessa, continuiamo con la prossima
if (betTypeId == 0) continue;
// Estrai e salva le quote
foreach (var value in bet["values"].AsArray())
{
// Gestiamo in modo più sicuro il recupero di valori dal JSON
string valueType = GetStringValueSafe(value, "value");
decimal oddValue = GetDecimalValueSafe(value, "odd");
// Log per debug
LogError($"Elaborazione quota: tipo={valueType}, valore={oddValue}", null);
if (oddValue > 0) // Verifichiamo che il valore sia valido
{
var query = @"
IF EXISTS (SELECT 1 FROM Odd WHERE fixture_id = @fixture_id AND bookmaker_id = @bookmaker_id
AND bet_type_id = @bet_type_id AND value = @value)
BEGIN
UPDATE Odd
SET odd = @odd, update_time = @update_time
WHERE fixture_id = @fixture_id AND bookmaker_id = @bookmaker_id
AND bet_type_id = @bet_type_id AND value = @value
END
ELSE
BEGIN
INSERT INTO Odd (fixture_id, bookmaker_id, bet_type_id, value, odd, update_time)
VALUES (@fixture_id, @bookmaker_id, @bet_type_id, @value, @odd, @update_time)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@fixture_id", fixtureId);
command.Parameters.AddWithValue("@bookmaker_id", bookmakerId);
command.Parameters.AddWithValue("@bet_type_id", betTypeId);
command.Parameters.AddWithValue("@value", valueType);
command.Parameters.AddWithValue("@odd", oddValue);
command.Parameters.AddWithValue("@update_time", DateTime.Now);
command.ExecuteNonQuery();
}
}
}
}
}
}
catch (Exception ex)
{
LogError("l'upsert delle quote", ex);
}
}
/// <summary>
/// Ottiene l'ID del tipo di scommessa in base al nome
/// </summary>
private int GetBetTypeId(SqlConnection connection, string betTypeName)
{
try
{
// Prima verifica se il tipo di scommessa esiste
var queryCheck = "SELECT bet_type_id FROM BetType WHERE name = @name";
using (var command = new SqlCommand(queryCheck, connection))
{
command.Parameters.AddWithValue("@name", betTypeName);
var result = command.ExecuteScalar();
if (result != null && result != DBNull.Value)
{
return Convert.ToInt32(result);
}
else
{
// Se non esiste, inseriscilo (con un ID progressivo)
var queryInsert = @"
DECLARE @next_id INT;
SELECT @next_id = ISNULL(MAX(bet_type_id), 0) + 1 FROM BetType;
INSERT INTO BetType (bet_type_id, name)
VALUES (@next_id, @name);
SELECT @next_id;";
using (var insertCommand = new SqlCommand(queryInsert, connection))
{
insertCommand.Parameters.AddWithValue("@name", betTypeName);
return Convert.ToInt32(insertCommand.ExecuteScalar());
}
}
}
}
catch (Exception ex)
{
LogError($"determinazione bet_type_id per '{betTypeName}'", ex);
return 0;
}
}
/// <summary>
/// Ottiene un valore stringa da un JsonNode in modo sicuro
/// </summary>
private string GetStringValueSafe(JsonNode node, string propertyName)
{
try
{
if (node == null || node[propertyName] == null)
return string.Empty;
var value = node[propertyName];
// Gestiamo diversi tipi di nodi
if (value.GetValueKind() == System.Text.Json.JsonValueKind.String)
return value.GetValue<string>();
else if (value.GetValueKind() == System.Text.Json.JsonValueKind.Number)
return value.GetValue<decimal>().ToString();
else
return value.ToString();
}
catch (Exception ex)
{
LogError($"estrazione valore stringa per proprietà '{propertyName}'", ex);
return string.Empty;
}
}
/// <summary>
/// Ottiene un valore decimale da un JsonNode in modo sicuro
/// </summary>
private decimal GetDecimalValueSafe(JsonNode node, string propertyName)
{
try
{
if (node == null || node[propertyName] == null)
return 0;
var value = node[propertyName];
// Gestiamo diversi tipi di nodi
if (value.GetValueKind() == System.Text.Json.JsonValueKind.Number)
{
// Assicuriamoci che il valore sia esattamente quello originale
return value.GetValue<decimal>();
}
else if (value.GetValueKind() == System.Text.Json.JsonValueKind.String)
{
// Utilizziamo InvariantCulture per garantire una corretta interpretazione dei decimali
if (decimal.TryParse(value.GetValue<string>(),
System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture,
out decimal result))
{
return result;
}
}
// Aggiungiamo un log più dettagliato per aiutare il debug
LogError($"Impossibile convertire '{value}' (tipo: {value.GetValueKind()}) in decimal per proprietà '{propertyName}'", null);
return 0;
}
catch (Exception ex)
{
LogError($"estrazione valore decimal per proprietà '{propertyName}'", ex);
return 0;
}
}
}
}
@@ -0,0 +1,112 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Prediction : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode predictions, int fixtureId)
{
try
{
// Estrazione dei dati dalla risposta JSON
string advice = predictions["advice"]?.GetValue<string>();
if (string.IsNullOrEmpty(advice)) return; // Nessuna previsione se non c'è consiglio
// Estrazione degli altri valori, con gestione null
int? winnerId = predictions["winner"]?["id"]?.GetValue<int?>();
string winnerName = predictions["winner"]?["name"]?.GetValue<string>();
string winnerComment = predictions["winner"]?["comment"]?.GetValue<string>();
bool? winOrDraw = predictions["win_or_draw"]?.GetValue<bool?>();
string underOver = predictions["under_over"]?.GetValue<string>();
string goalsHome = predictions["goals"]?["home"]?.GetValue<string>();
string goalsAway = predictions["goals"]?["away"]?.GetValue<string>();
string percentHome = predictions["percent"]?["home"]?.GetValue<string>();
string percentDraw = predictions["percent"]?["draw"]?.GetValue<string>();
string percentAway = predictions["percent"]?["away"]?.GetValue<string>();
// Query per l'upsert con tutti i campi disponibili
var query = @"
IF EXISTS (SELECT 1 FROM Prediction WHERE fixture_id = @fixture_id)
BEGIN
UPDATE Prediction
SET winner_id = @winner_id,
winner_name = @winner_name,
winner_comment = @winner_comment,
win_or_draw = @win_or_draw,
under_over = @under_over,
goals_home = @goals_home,
goals_away = @goals_away,
advice = @advice,
percent_home = @percent_home,
percent_draw = @percent_draw,
percent_away = @percent_away
WHERE fixture_id = @fixture_id
END
ELSE
BEGIN
INSERT INTO Prediction (
prediction_id,
fixture_id,
winner_id,
winner_name,
winner_comment,
win_or_draw,
under_over,
goals_home,
goals_away,
advice,
percent_home,
percent_draw,
percent_away
)
VALUES (
NEXT VALUE FOR prediction_seq,
@fixture_id,
@winner_id,
@winner_name,
@winner_comment,
@win_or_draw,
@under_over,
@goals_home,
@goals_away,
@advice,
@percent_home,
@percent_draw,
@percent_away
)
END";
using (var command = new SqlCommand(query, connection))
{
// Fixture ID
command.Parameters.AddWithValue("@fixture_id", fixtureId);
// Dati vincitore
command.Parameters.AddWithValue("@winner_id", winnerId.HasValue ? (object)winnerId.Value : DBNull.Value);
command.Parameters.AddWithValue("@winner_name", string.IsNullOrEmpty(winnerName) ? DBNull.Value : (object)winnerName);
command.Parameters.AddWithValue("@winner_comment", string.IsNullOrEmpty(winnerComment) ? DBNull.Value : (object)winnerComment);
// Altri dati di previsione
command.Parameters.AddWithValue("@win_or_draw", winOrDraw.HasValue ? (object)winOrDraw.Value : DBNull.Value);
command.Parameters.AddWithValue("@under_over", string.IsNullOrEmpty(underOver) ? DBNull.Value : (object)underOver);
command.Parameters.AddWithValue("@goals_home", string.IsNullOrEmpty(goalsHome) ? DBNull.Value : (object)goalsHome);
command.Parameters.AddWithValue("@goals_away", string.IsNullOrEmpty(goalsAway) ? DBNull.Value : (object)goalsAway);
command.Parameters.AddWithValue("@advice", advice); // Sappiamo che non è null grazie al controllo iniziale
// Percentuali
command.Parameters.AddWithValue("@percent_home", string.IsNullOrEmpty(percentHome) ? DBNull.Value : (object)percentHome);
command.Parameters.AddWithValue("@percent_draw", string.IsNullOrEmpty(percentDraw) ? DBNull.Value : (object)percentDraw);
command.Parameters.AddWithValue("@percent_away", string.IsNullOrEmpty(percentAway) ? DBNull.Value : (object)percentAway);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert della previsione", ex);
}
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Score : HorseRacingPredictor.Football.Manager.Database
{
public void Upsert(SqlConnection connection, JsonNode score, int fixtureId)
{
try
{
var query = @"
IF EXISTS (SELECT 1 FROM Score WHERE fixture_id = @fixture_id)
BEGIN
UPDATE Score SET home_halftime = @home_halftime, away_halftime = @away_halftime,
home_fulltime = @home_fulltime, away_fulltime = @away_fulltime,
home_extratime = @home_extratime, away_extratime = @away_extratime,
home_penalty = @home_penalty, away_penalty = @away_penalty
WHERE fixture_id = @fixture_id
END
ELSE
BEGIN
INSERT INTO Score (home_halftime, away_halftime, home_fulltime, away_fulltime, home_extratime, away_extratime, home_penalty, away_penalty, fixture_id)
VALUES (@home_halftime, @away_halftime, @home_fulltime, @away_fulltime, @home_extratime, @away_extratime, @home_penalty, @away_penalty, @fixture_id)
END";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@home_halftime", score["halftime"]["home"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@away_halftime", score["halftime"]["away"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@home_fulltime", score["fulltime"]["home"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@away_fulltime", score["fulltime"]["away"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@home_extratime", score["extratime"]["home"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@away_extratime", score["extratime"]["away"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@home_penalty", score["penalty"]["home"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@away_penalty", score["penalty"]["away"]?.GetValue<int?>() ?? 0);
command.Parameters.AddWithValue("@fixture_id", fixtureId);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError("l'upsert del punteggio", ex);
}
}
}
}
@@ -0,0 +1,77 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
namespace HorseRacingPredictor.Football.Database
{
internal class Team : HorseRacingPredictor.Football.Manager.Database
{
public void UpsertTeams(SqlConnection connection, SqlTransaction transaction, JsonNode teams, int fixtureId)
{
try
{
var homeTeamId = teams["home"]["id"]?.GetValue<int>();
var awayTeamId = teams["away"]["id"]?.GetValue<int>();
if (homeTeamId == null || awayTeamId == null) return; // Salta il record se uno degli id è null
var queryTeam = @"
IF NOT EXISTS (SELECT 1 FROM Team WHERE team_id = @team_id)
BEGIN
INSERT INTO Team (team_id, name, logo) VALUES (@team_id, @name, @logo)
END
ELSE
BEGIN
UPDATE Team SET name = @name, logo = @logo WHERE team_id = @team_id
END";
using (var command = new SqlCommand(queryTeam, connection, transaction))
{
var homeTeam = teams["home"];
var awayTeam = teams["away"];
// Upsert home team
command.Parameters.AddWithValue("@team_id", homeTeamId);
command.Parameters.AddWithValue("@name", homeTeam["name"]?.GetValue<string>() ?? "");
command.Parameters.AddWithValue("@logo", homeTeam["logo"]?.GetValue<string>() ?? "");
command.ExecuteNonQuery();
// Upsert away team
command.Parameters["@team_id"].Value = awayTeamId;
command.Parameters["@name"].Value = awayTeam["name"]?.GetValue<string>() ?? "";
command.Parameters["@logo"].Value = awayTeam["logo"]?.GetValue<string>() ?? "";
command.ExecuteNonQuery();
}
// Aggiungiamo la relazione fixture-team solo se fixtureId è valido (non 0)
if (fixtureId > 0)
{
var queryFixtureTeam = @"
IF NOT EXISTS (SELECT 1 FROM FixtureTeams WHERE fixture_id = @fixture_id)
BEGIN
INSERT INTO FixtureTeams (fixture_id, home_team_id, away_team_id, home_winner, away_winner)
VALUES (@fixture_id, @home_team_id, @away_team_id, NULL, NULL)
END
ELSE
BEGIN
UPDATE FixtureTeams SET home_team_id = @home_team_id, away_team_id = @away_team_id
WHERE fixture_id = @fixture_id
END";
using (var command = new SqlCommand(queryFixtureTeam, connection, transaction))
{
// Upsert fixture-team relationship
command.Parameters.AddWithValue("@fixture_id", fixtureId);
command.Parameters.AddWithValue("@home_team_id", homeTeamId);
command.Parameters.AddWithValue("@away_team_id", awayTeamId);
command.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
LogError("l'upsert delle squadre", ex);
throw; // Rilancia l'eccezione per il rollback della transazione
}
}
}
}
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace HorseRacingPredictor.Football.Database
{
internal class TeamStats : HorseRacingPredictor.Football.Manager.Database
{
public int Insert(SqlConnection connection, int? teamId, int? predictionId, bool isHome, JsonNode stats)
{
try
{
// Ottieni il prossimo ID di statistiche
int statsId = GetNextStatsId(connection);
if (statsId <= 0) return 0;
var query = @"
INSERT INTO TeamStats (
stats_id, team_id, prediction_id, is_home, played, form,
att, def, goals_for_total, goals_for_average,
goals_against_total, goals_against_average
) VALUES (
@stats_id, @team_id, @prediction_id, @is_home, @played, @form,
@att, @def, @goals_for_total, @goals_for_average,
@goals_against_total, @goals_against_average
)";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@stats_id", statsId);
command.Parameters.AddWithValue("@team_id", teamId.HasValue ? (object)teamId.Value : DBNull.Value);
command.Parameters.AddWithValue("@prediction_id", predictionId.HasValue ? (object)predictionId.Value : DBNull.Value);
command.Parameters.AddWithValue("@is_home", isHome);
command.Parameters.AddWithValue("@played", stats["played"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@form", stats["form"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@att", stats["att"]?.GetValue<string>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@def", stats["def"]?.GetValue<string>() ?? (object)DBNull.Value);
var goalsFor = stats["goals"]?["for"];
command.Parameters.AddWithValue("@goals_for_total", goalsFor?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@goals_for_average", goalsFor?["average"]?.GetValue<string>() ?? (object)DBNull.Value);
var goalsAgainst = stats["goals"]?["against"];
command.Parameters.AddWithValue("@goals_against_total", goalsAgainst?["total"]?.GetValue<int?>() ?? (object)DBNull.Value);
command.Parameters.AddWithValue("@goals_against_average", goalsAgainst?["average"]?.GetValue<string>() ?? (object)DBNull.Value);
command.ExecuteNonQuery();
}
return statsId;
}
catch (Exception ex)
{
LogError("l'inserimento delle statistiche di squadra", ex);
return 0;
}
}
private int GetNextStatsId(SqlConnection connection)
{
try
{
var query = "SELECT ISNULL(MAX(stats_id), 0) + 1 FROM TeamStats";
using (var command = new SqlCommand(query, connection))
{
return Convert.ToInt32(command.ExecuteScalar());
}
}
catch (Exception ex)
{
LogError("il recupero del prossimo ID statistiche", ex);
return 0;
}
}
}
}
@@ -0,0 +1,852 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RestSharp;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Data.SqlClient;
namespace HorseRacingPredictor.Football
{
/// <summary>
/// Classe centralizzata per la gestione delle API di Football
/// </summary>
public class Main
{
private readonly Manager.Database _database;
private readonly API.Prediction _predictionManager;
private readonly API.Fixture _fixtureAPI;
private readonly API.Odds _oddsAPI;
// Repository utilizzati per la gestione dei dati
private readonly Football.Database.League _leagueRepository;
private readonly Football.Database.Fixture _fixtureRepository;
private readonly Football.Database.Team _teamRepository;
private readonly Football.Database.Goals _goalsRepository;
private readonly Football.Database.Score _scoreRepository;
private readonly Football.Database.Odds _oddsRepository;
private readonly Football.Database.Prediction _predictionRepository;
// Nuovi repository aggiunti
private readonly Football.Database.BetType _betTypeRepository;
private readonly Football.Database.Bookmaker _bookmakerRepository;
private readonly Football.Database.FixtureLeague _fixtureLeagueRepository;
private readonly Football.Database.Comparison _comparisonRepository;
private readonly Football.Database.H2H _h2hRepository;
private readonly Football.Database.LeagueStats _leagueStatsRepository;
private readonly Football.Database.TeamStats _teamStatsRepository;
// Aggiungi il repository per le risposte API
private readonly Football.Database.APIResponse _apiResponseRepository;
public Main()
{
_database = new Manager.Database();
_predictionManager = new API.Prediction();
_fixtureAPI = new API.Fixture();
_oddsAPI = new API.Odds();
// Inizializzazione dei repository qui invece che nella classe Database
_leagueRepository = new Football.Database.League();
_fixtureRepository = new Football.Database.Fixture();
_teamRepository = new Football.Database.Team();
_goalsRepository = new Football.Database.Goals();
_scoreRepository = new Football.Database.Score();
_oddsRepository = new Football.Database.Odds();
_predictionRepository = new Football.Database.Prediction();
// Inizializzazione dei nuovi repository
_betTypeRepository = new Football.Database.BetType();
_bookmakerRepository = new Football.Database.Bookmaker();
_fixtureLeagueRepository = new Football.Database.FixtureLeague();
_comparisonRepository = new Football.Database.Comparison();
_h2hRepository = new Football.Database.H2H();
_leagueStatsRepository = new Football.Database.LeagueStats();
_teamStatsRepository = new Football.Database.TeamStats();
// Inizializzazione del repository per le risposte API
_apiResponseRepository = new Football.Database.APIResponse();
}
/// <summary>
/// Metodo centralizzato per ottenere tutte le partite e le relative quote per una data specifica
/// Utilizzo della tabella API_Response come intermediario
/// </summary>
/// <param name="date">Data per cui recuperare le partite</param>
/// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
/// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
/// <returns>DataTable contenente tutte le partite con le relative quote (quando disponibili)</returns>
public DataTable GetFixturesAndOdds(DateTime date, IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
{
try
{
// Prima scarica i dati
DownloadFixturesAndOdds(date, progressCallback, statusCallback).Wait();
// Poi importa i dati
return ImportFromApiResponses(progressCallback, statusCallback);
}
catch (Exception ex)
{
_database.LogError("recupero centralizzato di partite e quote", ex);
statusCallback?.Report($"Errore: {ex.Message}");
return CreateEmptyResultTable();
}
}
/// <summary>
/// Scarica i dati dalle API e li salva nella tabella di frontiera API_Response
/// </summary>
/// <param name="date">Data per cui recuperare le partite</param>
/// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
/// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
public async Task DownloadFixturesAndOdds(DateTime date, IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
{
try
{
// Step 1: Informare l'utente che stiamo ottenendo le partite
statusCallback?.Report("Scaricamento delle partite in corso...");
progressCallback?.Report(0);
// Step 2: Recuperare tutte le partite per la data selezionata
var fixturesResponse = GetFixtures(date);
progressCallback?.Report(20);
// Step 3: Informare l'utente che stiamo recuperando le quote
statusCallback?.Report("Scaricamento delle quote in corso...");
progressCallback?.Report(40);
// Step 4: Recuperare le quote per la data selezionata
var oddsResponses = GetOdds(date);
progressCallback?.Report(60);
// Step 5: Informare l'utente che stiamo recuperando le previsioni
statusCallback?.Report("Scaricamento delle previsioni in corso...");
progressCallback?.Report(70);
// Step 6: Recuperare le previsioni per le partite
await GetPredictionsForFixtures(fixturesResponse);
progressCallback?.Report(90);
// Step 7: Operazione completata
statusCallback?.Report("Scaricamento completato. Dati salvati nella tabella di frontiera.");
progressCallback?.Report(100);
}
catch (Exception ex)
{
_database.LogError("scaricamento centralizzato di partite e quote", ex);
statusCallback?.Report($"Errore: {ex.Message}");
throw;
}
}
/// <summary>
/// Recupera le previsioni per le partite specificate
/// </summary>
private async Task GetPredictionsForFixtures(RestResponse fixturesResponse)
{
try
{
// Verifica che la risposta sia valida
if (fixturesResponse == null || !fixturesResponse.IsSuccessful || string.IsNullOrEmpty(fixturesResponse.Content))
{
return;
}
var json = JsonDocument.Parse(fixturesResponse.Content).RootElement;
// Verifica che la risposta contenga dati validi
if (!json.TryGetProperty("response", out var responseElement))
{
return;
}
// Per ogni partita, recupera la previsione
foreach (var item in responseElement.EnumerateArray())
{
try
{
int fixtureId = item.GetProperty("fixture").GetProperty("id").GetInt32();
// Utilizza il prediction manager per ottenere la previsione
_predictionManager.GetPredictionByFixture(fixtureId);
// Breve pausa per non sovraccaricare l'API
await Task.Delay(100);
}
catch (Exception ex)
{
_database.LogError($"recupero previsione", ex);
// Continua con la prossima partita
}
}
}
catch (Exception ex)
{
_database.LogError("recupero previsioni per partite", ex);
}
}
/// <summary>
/// Importa i dati dalla tabella di frontiera API_Response alle tabelle finali
/// </summary>
/// <param name="progressCallback">Callback per aggiornare il progresso dell'operazione</param>
/// <param name="statusCallback">Callback per aggiornare lo stato dell'operazione</param>
/// <returns>DataTable contenente tutte le partite con le relative quote (quando disponibili)</returns>
public DataTable ImportFromApiResponses(IProgress<int> progressCallback = null, IProgress<string> statusCallback = null)
{
try
{
// Step 1: Informare l'utente che stiamo elaborando le risposte API
statusCallback?.Report("Elaborazione delle risposte API in corso...");
progressCallback?.Report(0);
// Step 2: Elaborare le risposte API non elaborate dal database
ProcessUnprocessedApiResponses();
progressCallback?.Report(50);
// Step 3: Creare un DataTable vuoto per i fixture
var fixturesTable = CreateEmptyFixturesDataTable();
// Step 4: Recuperare i dati elaborati dal database per visualizzarli
statusCallback?.Report("Preparazione dei dati per la visualizzazione...");
var combinedTable = GetProcessedFixturesFromDatabase();
progressCallback?.Report(90);
// Step 5: Operazione completata
statusCallback?.Report("Importazione completata.");
progressCallback?.Report(100);
return combinedTable;
}
catch (Exception ex)
{
_database.LogError("importazione centralizzata di partite e quote", ex);
statusCallback?.Report($"Errore: {ex.Message}");
return CreateEmptyResultTable();
}
}
/// <summary>
/// Recupera le partite per la data specificata utilizzando API.Fixture
/// </summary>
private RestResponse GetFixtures(DateTime date)
{
return _fixtureAPI.GetFixturesByDate(date);
}
/// <summary>
/// Recupera le quote per la data specificata (potenzialmente più pagine) utilizzando API.Odds
/// </summary>
private List<RestResponse> GetOdds(DateTime date)
{
var responses = new List<RestResponse>();
int currentPage = 1;
int maxPages = 3; // Limita a 3 pagine per evitare troppe chiamate API
bool hasMorePages = true;
while (hasMorePages && currentPage <= maxPages)
{
var response = _oddsAPI.GetOddsByDate(date, 8, currentPage); // ID 8 = Bet365
responses.Add(response);
// Controlla se ci sono altre pagine
var json = JsonDocument.Parse(response.Content).RootElement;
if (json.TryGetProperty("paging", out var pagingElement) &&
pagingElement.TryGetProperty("total", out var totalElement))
{
int totalPages = totalElement.GetInt32();
hasMorePages = currentPage < totalPages;
}
else
{
hasMorePages = false;
}
currentPage++;
}
return responses;
}
/// <summary>
/// Elabora i dati JSON e li inserisce nel database utilizzando transazioni e disabilitazione temporanea dei vincoli
/// </summary>
/// <param name="jsonResponse">Risposta JSON da elaborare</param>
public void ProcessAndInsertData(string jsonResponse)
{
try
{
var jsonObject = JsonNode.Parse(jsonResponse);
_database.ExecuteTransactionalQuery("l'elaborazione dei dati calcistici", (connection, transaction) =>
{
try
{
// Disabilita temporaneamente i vincoli di chiave esterna
((Manager.Database)_database).DisableAllConstraints(connection, transaction);
// FASE 1: Inserisci le leghe
foreach (var responseItem in jsonObject["response"].AsArray())
{
var league = responseItem["league"];
if (league != null && league["id"] != null)
{
_leagueRepository.Upsert(connection, transaction, league);
}
}
// FASE 2: Inserisci i bookmakers e i tipi di scommessa
foreach (var responseItem in jsonObject["response"].AsArray())
{
if (responseItem["bookmakers"] != null)
{
foreach (var bookmaker in responseItem["bookmakers"].AsArray())
{
if (bookmaker["id"] != null)
{
_bookmakerRepository.Upsert(connection, bookmaker);
// Tipi di scommessa
if (bookmaker["bets"] != null)
{
foreach (var bet in bookmaker["bets"].AsArray())
{
if (bet["id"] != null && bet["name"] != null)
{
_betTypeRepository.Upsert(connection, bet);
}
}
}
}
}
}
}
// FASE 3: Inserisci le squadre (senza relazione con fixture)
foreach (var responseItem in jsonObject["response"].AsArray())
{
var teams = responseItem["teams"];
if (teams != null && teams["home"] != null && teams["away"] != null &&
teams["home"]["id"] != null && teams["away"]["id"] != null)
{
// Usa 0 come placeholder per fixtureId - in questa fase inseriamo solo le squadre
_teamRepository.UpsertTeams(connection, transaction, teams, 0);
}
}
// FASE 4: Inserisci i fixture e venue
foreach (var responseItem in jsonObject["response"].AsArray())
{
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
{
_fixtureRepository.Upsert(connection, transaction, fixture);
}
}
// FASE 5: Inserisci relazioni tra entità e dati dipendenti
foreach (var responseItem in jsonObject["response"].AsArray())
{
int? fixtureId = null;
try
{
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
{
fixtureId = fixture["id"].GetValue<int>();
// Relazioni fixture-team
var teams = responseItem["teams"];
if (teams != null && teams["home"] != null && teams["away"] != null &&
teams["home"]["id"] != null && teams["away"]["id"] != null)
{
_teamRepository.UpsertTeams(connection, transaction, teams, fixtureId.Value);
}
// Relazioni fixture-league
var league = responseItem["league"];
if (league != null && league["id"] != null)
{
int leagueId = league["id"].GetValue<int>();
_fixtureLeagueRepository.Upsert(connection, transaction, fixtureId.Value, leagueId, league);
}
// Goals
var goals = responseItem["goals"];
if (goals != null)
{
_goalsRepository.Upsert(connection, goals, fixtureId.Value);
}
// Score
var score = responseItem["score"];
if (score != null)
{
_scoreRepository.Upsert(connection, score, fixtureId.Value);
}
}
}
catch (Exception ex)
{
_database.LogError($"inserimento dati dipendenti per fixture {fixtureId}", ex);
// Continuiamo con il prossimo elemento, non interrompiamo l'intera transazione
}
}
// FASE 6: Inserisci dati che richiedono fixture e teams: quote e previsioni
foreach (var responseItem in jsonObject["response"].AsArray())
{
int? fixtureId = null;
try
{
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
{
fixtureId = fixture["id"].GetValue<int>();
// Quote
if (responseItem["bookmakers"] != null)
{
_oddsRepository.Upsert(connection, responseItem["bookmakers"], fixtureId.Value);
}
// Previsioni
var predictions = responseItem["predictions"];
if (predictions != null)
{
_predictionRepository.Upsert(connection, predictions, fixtureId.Value);
// Recupera ID previsione
int? predictionId = null;
using (var cmd = new SqlCommand("SELECT prediction_id FROM Prediction WHERE fixture_id = @fixture_id", connection, transaction))
{
cmd.Parameters.AddWithValue("@fixture_id", fixtureId.Value);
var result = cmd.ExecuteScalar();
if (result != null && result != DBNull.Value)
{
predictionId = Convert.ToInt32(result);
}
}
if (predictionId.HasValue)
{
// Confronto
if (predictions["comparison"] != null)
{
_comparisonRepository.Upsert(connection, predictionId.Value, predictions["comparison"]);
}
// Head-to-head
if (predictions["h2h"] != null && predictions["h2h"].AsArray().Any())
{
_h2hRepository.DeleteForPrediction(connection, predictionId.Value);
foreach (var h2hFixture in predictions["h2h"].AsArray())
{
if (h2hFixture["fixture"] != null && h2hFixture["fixture"]["id"] != null)
{
_h2hRepository.Insert(connection, predictionId.Value, h2hFixture["fixture"]["id"].GetValue<int>());
}
}
}
// Statistiche
var teams = responseItem["teams"];
if (predictions["teams"] != null && teams != null)
{
// Home team stats
if (teams["home"] != null && teams["home"]["id"] != null && predictions["teams"]["home"] != null)
{
int homeTeamId = teams["home"]["id"].GetValue<int>();
if (predictions["teams"]["home"]["team"] != null)
{
_teamStatsRepository.Insert(connection, homeTeamId, predictionId.Value, true,
predictions["teams"]["home"]["team"]);
}
if (predictions["teams"]["home"]["league"] != null)
{
_leagueStatsRepository.Insert(connection, homeTeamId, predictionId.Value, true,
predictions["teams"]["home"]["league"]);
}
}
// Away team stats
if (teams["away"] != null && teams["away"]["id"] != null && predictions["teams"]["away"] != null)
{
int awayTeamId = teams["away"]["id"].GetValue<int>();
if (predictions["teams"]["away"]["team"] != null)
{
_teamStatsRepository.Insert(connection, awayTeamId, predictionId.Value, false,
predictions["teams"]["away"]["team"]);
}
if (predictions["teams"]["away"]["league"] != null)
{
_leagueStatsRepository.Insert(connection, awayTeamId, predictionId.Value, false,
predictions["teams"]["away"]["league"]);
}
}
}
}
}
}
}
catch (Exception ex)
{
_database.LogError($"inserimento previsioni e quote per fixture {fixtureId}", ex);
// Continuiamo con il prossimo elemento
}
}
// Riattiva i vincoli
((Manager.Database)_database).EnableAllConstraints(connection, transaction);
}
catch (Exception ex)
{
_database.LogError("processamento dati in transazione", ex);
throw; // Rilancia l'eccezione per il rollback della transazione
}
});
}
catch (Exception ex)
{
_database.LogError("l'elaborazione dei dati calcistici", ex);
}
}
/// <summary>
/// Aggiorna il database con i dati di partite e quote utilizzando le classi del namespace Football.Database
/// </summary>
private void UpdateDatabase(RestResponse fixturesResponse, List<RestResponse> oddsResponses)
{
try
{
// In questo metodo non elaboriamo più direttamente le risposte
// Le risposte sono già state salvate nel database dalla classe API
// e verranno elaborate dal metodo ProcessUnprocessedApiResponses
// Processa le risposte non elaborate
ProcessUnprocessedApiResponses();
}
catch (Exception ex)
{
_database.LogError("aggiornamento database", ex);
throw; // Propaga l'eccezione per gestirla al livello superiore
}
}
/// <summary>
/// Crea un DataTable vuoto per i fixture
/// </summary>
private DataTable CreateEmptyFixturesDataTable()
{
var dataTable = new DataTable();
// Aggiungi colonne
dataTable.Columns.Add("ID", typeof(int));
dataTable.Columns.Add("Paese", typeof(string));
dataTable.Columns.Add("Campionato", typeof(string));
dataTable.Columns.Add("Data / Ora", typeof(DateTime));
dataTable.Columns.Add("Stato", typeof(string));
dataTable.Columns.Add("Casa", typeof(string));
dataTable.Columns.Add("Trasferta", typeof(string));
dataTable.Columns.Add("Goals Casa", typeof(int));
dataTable.Columns.Add("Goals Trasferta", typeof(int));
dataTable.Columns.Add("Quota Casa", typeof(string));
dataTable.Columns.Add("Quota Pareggio", typeof(string));
dataTable.Columns.Add("Quota Trasferta", typeof(string));
dataTable.Columns.Add("Previsione", typeof(string));
return dataTable;
}
/// <summary>
/// Crea un DataTable con le partite dalla risposta API
/// </summary>
private DataTable CreateFixturesDataTable(RestResponse response)
{
var dataTable = CreateEmptyFixturesDataTable();
// Verifica che la risposta sia valida
if (response == null || !response.IsSuccessful || string.IsNullOrEmpty(response.Content))
{
return dataTable;
}
try
{
var json = JsonDocument.Parse(response.Content).RootElement;
// Verifica che la risposta contenga dati validi
if (!json.TryGetProperty("response", out var responseElement))
{
return dataTable;
}
// Aggiungi righe
foreach (var item in responseElement.EnumerateArray())
{
try
{
var row = dataTable.NewRow();
// Estrai i dati dalla risposta JSON
row["ID"] = item.GetProperty("fixture").GetProperty("id").GetInt32();
row["Paese"] = item.GetProperty("league").GetProperty("country").GetString();
row["Campionato"] = item.GetProperty("league").GetProperty("name").GetString();
row["Data / Ora"] = DateTime.Parse(item.GetProperty("fixture").GetProperty("date").GetString());
row["Stato"] = item.GetProperty("fixture").GetProperty("status").GetProperty("long").GetString();
row["Casa"] = item.GetProperty("teams").GetProperty("home").GetProperty("name").GetString();
row["Trasferta"] = item.GetProperty("teams").GetProperty("away").GetProperty("name").GetString();
// Per i goals, controlla se sono null
var goalsElement = item.GetProperty("goals");
row["Goals Casa"] = goalsElement.GetProperty("home").ValueKind == JsonValueKind.Null ?
0 : goalsElement.GetProperty("home").GetInt32();
row["Goals Trasferta"] = goalsElement.GetProperty("away").ValueKind == JsonValueKind.Null ?
0 : goalsElement.GetProperty("away").GetInt32();
// Le colonne delle quote e previsione rimangono temporaneamente vuote
row["Quota Casa"] = DBNull.Value;
row["Quota Pareggio"] = DBNull.Value;
row["Quota Trasferta"] = DBNull.Value;
row["Previsione"] = DBNull.Value;
dataTable.Rows.Add(row);
}
catch (Exception ex)
{
_database.LogError($"elaborazione riga partita", ex);
// Continua con la prossima partita
}
}
}
catch (Exception ex)
{
_database.LogError("creazione tabella partite", ex);
// Ritorna la tabella vuota in caso di errore
}
return dataTable;
}
/// <summary>
/// Combina i dati delle partite con le quote
/// </summary>
private DataTable CombineFixturesAndOdds(DataTable fixturesTable, List<RestResponse> oddsResponses)
{
// Crea una copia del DataTable delle partite
var combinedTable = fixturesTable.Copy();
// Se non ci sono risposte di quote o la tabella delle partite è vuota, ritorna la tabella originale
if (oddsResponses == null || oddsResponses.Count == 0 || combinedTable.Rows.Count == 0)
{
return combinedTable;
}
try
{
// Elabora ogni risposta delle quote
foreach (var response in oddsResponses)
{
if (!response.IsSuccessful || string.IsNullOrEmpty(response.Content))
{
continue;
}
var json = JsonDocument.Parse(response.Content).RootElement;
if (!json.TryGetProperty("response", out var responseElement))
{
continue;
}
foreach (var item in responseElement.EnumerateArray())
{
try
{
int fixtureId = item.GetProperty("fixture").GetProperty("id").GetInt32();
// Trova la riga corrispondente nella tabella delle partite
DataRow[] matchingRows = combinedTable.Select($"ID = {fixtureId}");
if (matchingRows.Length == 0) continue;
DataRow row = matchingRows[0];
// Cerca le quote del bookmaker (assumiamo Bet365 con ID 8)
if (item.TryGetProperty("bookmakers", out var bookmakersElement))
{
foreach (var bookmaker in bookmakersElement.EnumerateArray())
{
if (bookmaker.GetProperty("id").GetInt32() == 8) // Bet365
{
if (bookmaker.TryGetProperty("bets", out var betsElement))
{
foreach (var bet in betsElement.EnumerateArray())
{
if (bet.GetProperty("name").GetString() == "Match Winner")
{
// Elabora le quote 1X2
foreach (var value in bet.GetProperty("values").EnumerateArray())
{
string betType = value.GetProperty("value").GetString();
string odd = value.GetProperty("odd").GetString();
switch (betType)
{
case "Home":
row["Quota Casa"] = odd;
break;
case "Draw":
row["Quota Pareggio"] = odd;
break;
case "Away":
row["Quota Trasferta"] = odd;
break;
}
}
break;
}
}
}
break;
}
}
}
// Recupera la previsione per questa partita
try
{
var predictionResponse = _predictionManager.GetPredictionByFixture(fixtureId);
if (predictionResponse != null && predictionResponse.IsSuccessful)
{
var predictionJson = JsonDocument.Parse(predictionResponse.Content).RootElement;
if (predictionJson.TryGetProperty("response", out var predResponse) &&
predResponse.EnumerateArray().Any())
{
var prediction = predResponse.EnumerateArray().First();
if (prediction.TryGetProperty("predictions", out var predsElement) &&
predsElement.TryGetProperty("winner", out var winnerElement) &&
winnerElement.TryGetProperty("name", out var winnerNameElement))
{
row["Previsione"] = winnerNameElement.GetString();
}
}
}
}
catch (Exception ex)
{
_database.LogError($"recupero previsione per partita ID: {fixtureId}", ex);
// Continua con la prossima partita
}
}
catch (Exception ex)
{
_database.LogError("elaborazione quote partita", ex);
// Continua con la prossima partita
}
}
}
}
catch (Exception ex)
{
_database.LogError("combinazione partite e quote", ex);
// In caso di errore, ritorna la tabella originale
}
// Ordina le righe per data
try
{
combinedTable.DefaultView.Sort = "Data / Ora ASC";
return combinedTable.DefaultView.ToTable();
}
catch
{
return combinedTable; // In caso di errore nell'ordinamento, ritorna la tabella non ordinata
}
}
/// <summary>
/// Crea un DataTable vuoto in caso di errore
/// </summary>
private DataTable CreateEmptyResultTable()
{
var table = new DataTable();
table.Columns.Add("Errore", typeof(string));
var row = table.NewRow();
row["Errore"] = "Si è verificato un errore durante il recupero dei dati.";
table.Rows.Add(row);
return table;
}
/// <summary>
/// Elabora le risposte API non elaborate presenti nel database
/// </summary>
private void ProcessUnprocessedApiResponses()
{
try
{
// Recupera tutte le risposte non elaborate
var unprocessedResponses = _apiResponseRepository.GetUnprocessedResponses();
foreach (DataRow responseRow in unprocessedResponses.Rows)
{
int responseId = Convert.ToInt32(responseRow["id"]);
string content = responseRow["response_content"].ToString();
string apiType = responseRow["api_type"].ToString();
try
{
// Processa il contenuto della risposta API
ProcessAndInsertData(content);
// Aggiorna lo stato della risposta come elaborata
_apiResponseRepository.UpdateProcessingStatus(responseId, true);
}
catch (Exception ex)
{
// Aggiorna lo stato della risposta con l'errore
_apiResponseRepository.UpdateProcessingStatus(responseId, false, ex.Message);
_database.LogError($"elaborazione risposta API ID: {responseId}", ex);
}
}
}
catch (Exception ex)
{
_database.LogError("elaborazione risposte API non elaborate", ex);
throw;
}
}
/// <summary>
/// Recupera i fixture elaborati dal database
/// </summary>
private DataTable GetProcessedFixturesFromDatabase()
{
try
{
// Step 1: Creare un DataTable vuoto per i fixture
var result = CreateEmptyFixturesDataTable();
// Step 2: Delegare alla classe repository appropriata il recupero dei dati
// Utilizziamo principalmente il repository Fixture che può coordinarsi con gli altri repository
return _fixtureRepository.GetProcessedFixtures();
}
catch (Exception ex)
{
_database.LogError("recupero partite elaborate dal database", ex);
// Return empty table in case of error
return CreateEmptyFixturesDataTable();
}
}
}
}
@@ -0,0 +1,149 @@
using System;
using System.Threading.Tasks;
using RestSharp;
using HorseRacingPredictor.Football.Database;
using HorseRacingPredictor.Football.API;
using League = HorseRacingPredictor.Football.API.League;
using Fixture = HorseRacingPredictor.Football.API.Fixture;
namespace HorseRacingPredictor.Football.Manager
{
internal class API : HorseRacingPredictor.Manager.API
{
// Configurazione dell'API
protected const string ApiKey = "f3795ccef056c5478d316162517d9970";
protected const string KeyHeader = "x-rapidapi-key";
protected const string HostHeader = "x-rapidapi-host";
protected const string HostValue = "v3.football.api-sports.io";
protected const string BaseUrl = "https://v3.football.api-sports.io";
// Repository per le risposte API
private readonly APIResponse _apiResponseRepository;
// Costruttore
public API()
{
_apiResponseRepository = new APIResponse();
}
// Tipi di API per il salvataggio nel database
public static class ApiTypes
{
public const string Fixtures = "FootballFixtures";
public const string Odds = "FootballOdds";
public const string Predictions = "FootballPredictions";
public const string Leagues = "FootballLeagues";
public const string Teams = "FootballTeams";
}
/// <summary>
/// Esegue una richiesta API, salva la risposta nel database e restituisce la risposta
/// </summary>
protected RestResponse ExecuteRequest(string endpoint, string apiType = null, string parameters = null)
{
try
{
string url = $"{BaseUrl}/{endpoint}";
var response = ExecuteApiRequest(url, ApiKey, KeyHeader, HostHeader, HostValue);
// Salva la risposta su file per debug
SaveResponseToFile(url, response.Content, "FootballApiResponses");
// Salva la risposta nel database
if (apiType != null)
{
_apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
}
return response;
}
catch (Exception ex)
{
throw new Exception($"Errore durante la richiesta all'endpoint {endpoint}: {ex.Message}", ex);
}
}
/// <summary>
/// Versione asincrona di ExecuteRequest
/// </summary>
protected async Task<RestResponse> ExecuteRequestAsync(string endpoint, string apiType = null, string parameters = null)
{
try
{
string url = $"{BaseUrl}/{endpoint}";
var response = await ExecuteApiRequestAsync(url, ApiKey, KeyHeader, HostHeader, HostValue);
// Salva la risposta su file per debug
SaveResponseToFile(url, response.Content, "FootballApiResponses");
// Salva la risposta nel database
if (apiType != null)
{
_apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
}
return response;
}
catch (Exception ex)
{
throw new Exception($"Errore durante la richiesta all'endpoint {endpoint}: {ex.Message}", ex);
}
}
#region Leagues
/// <summary>
/// Ottiene informazioni sui campionati
/// </summary>
public RestResponse GetLeagues()
{
return new League().GetLeagues();
}
/// <summary>
/// Versione asincrona di GetLeagues
/// </summary>
public async Task<RestResponse> GetLeaguesAsync()
{
return await new League().GetLeaguesAsync();
}
/// <summary>
/// Ottiene informazioni su una specifica stagione di una lega
/// </summary>
public RestResponse GetLeagueBySeason(int leagueId, int season)
{
return new League().GetLeagueBySeason(leagueId, season);
}
#endregion
#region Fixtures
/// <summary>
/// Ottiene le partite per una data specifica
/// </summary>
public RestResponse GetFixturesByDate(DateTime date)
{
return new Fixture().GetFixturesByDate(date);
}
/// <summary>
/// Versione asincrona di GetFixturesByDate
/// </summary>
public async Task<RestResponse> GetFixturesByDateAsync(DateTime date)
{
return await new Fixture().GetFixturesByDateAsync(date);
}
/// <summary>
/// Ottiene le partite per un ID specifico
/// </summary>
public RestResponse GetFixtureById(int fixtureId)
{
return new Fixture().GetFixtureById(fixtureId);
}
#endregion
}
}
@@ -0,0 +1,60 @@
using System;
using System.Data.SqlClient;
using System.Text.Json.Nodes;
using HorseRacingPredictor.Football.Database;
namespace HorseRacingPredictor.Football.Manager
{
internal class Database : HorseRacingPredictor.Manager.Database
{
// Implementazione della proprietà astratta _connectionString per il database calcio
protected override string _connectionString => "Server=DESKTOP-9O9JHFS;Database=TestBS_Football;User Id=sa;Password=Asti2019;";
// Usato il modificatore "new" per evitare il warning CS0108
protected new void LogError(string operation, Exception ex)
{
base.LogError(operation, ex);
}
// Usato il modificatore "new" per evitare il warning CS0108
protected new void ExecuteQuery(string operation, Action<SqlConnection> action)
{
base.ExecuteQuery(operation, action);
}
// Aggiunto il modificatore "new" per evitare il warning CS0108
protected new void ExecuteTransactionalQuery(string operation, Action<SqlConnection, SqlTransaction> action)
{
base.ExecuteTransactionalQuery(operation, action);
}
// Questo metodo ora è vuoto e sarà implementato nella Main
public void ProcessAndInsertData(string jsonResponse)
{
// Il codice è stato spostato in Football.Main
LogError("l'elaborazione dei dati calcistici", new NotImplementedException("Questo metodo è stato spostato in Football.Main"));
}
/// <summary>
/// Disabilita temporaneamente tutti i vincoli di chiave esterna nel database
/// </summary>
public void DisableAllConstraints(SqlConnection connection, SqlTransaction transaction)
{
using (var cmd = new SqlCommand("EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'", connection, transaction))
{
cmd.ExecuteNonQuery();
}
}
/// <summary>
/// Riattiva tutti i vincoli di chiave esterna nel database
/// </summary>
public void EnableAllConstraints(SqlConnection connection, SqlTransaction transaction)
{
using (var cmd = new SqlCommand("EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'", connection, transaction))
{
cmd.ExecuteNonQuery();
}
}
}
}
@@ -0,0 +1,246 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HorseRacingPredictor.Horses.ML;
namespace HorseRacingPredictor.Horses
{
internal class Calculator
{
private readonly MachineLearningService _mlService;
public Calculator()
{
_mlService = new MachineLearningService();
}
public int CalculateRating1(DataRow row)
{
/* HORSE RATING */
// Implementa la logica per calcolare il rating del cavallo
// Puoi usare i campi come "Handicap Rating", "Career Wins", "Jockey Last 100 Wins", ecc.
double rating = 0;
// Esempio di calcolo del rating (puoi personalizzarlo in base alle tue esigenze)
rating += GetValueOrDefault(row, "Handicap Rating");
rating += GetValueOrDefault(row, "Career Wins") * 10;
rating += GetValueOrDefault(row, "Jockey Last 100 Wins") * 5;
return (int)rating;
}
public int CalculateRating2(DataRow row)
{
/* AI RATING */
// Calcola il punteggio IA basato sullo script fornito
double rating = 0;
rating += GetValueOrDefault(row, "Handicap Rating") * 0.3;
rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;
rating += GetValueOrDefault(row, "This Track Strike Rate") * 100 * 0.1;
rating += GetValueOrDefault(row, "This Distance Strike Rate") * 100 * 0.1;
rating += GetValueOrDefault(row, "Jockey Last 100 Strike Rate") * 100 * 0.1;
rating += GetValueOrDefault(row, "Trainer Last 100 Strike Rate") * 100 * 0.1;
// Limita il punteggio a un valore intero
return (int)rating;
}
public int CalculateRating3(DataRow row)
{
/* COPILOT RATING */
// Calcola il punteggio Copilot basato sui campi del file CSV
double rating = 0;
// Esempio di calcolo del rating con pesi assegnati a ciascuna statistica
rating += GetValueOrDefault(row, "Handicap Rating") * 0.4;
rating += GetValueOrDefault(row, "Career Wins") * 0.3;
rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;
return (int)rating;
}
public int CalculateRating4(DataRow row)
{
double rating = 0;
rating += GetValueOrDefault(row, "Handicap Rating") * 0.3;
rating += GetValueOrDefault(row, "Career Wins") * 0.2;
rating += GetValueOrDefault(row, "Career Strike Rate") * 100 * 0.2;
rating += GetValueOrDefault(row, "Average Prize Money") * 0.0001 * 0.1;
rating += GetValueOrDefault(row, "This Track Wins") * 0.1;
rating += GetValueOrDefault(row, "This Distance Wins") * 0.1;
rating += GetValueOrDefault(row, "Jockey Last 100 Wins") * 0.1;
rating += GetValueOrDefault(row, "Trainer Last 100 Wins") * 0.1;
return (int)rating;
}
public class RaceContext
{
public bool IsWetTrack { get; set; }
public double DistanceDelta { get; set; }
public int MaxBarrier { get; set; }
public double AvgWeight { get; set; }
public double MaxPrizeMoney { get; set; }
}
public double CalculateRating5(DataRow row)
{
double score = 0;
// 1. CORE HORSE PROFILE (15%)
// Età ottimale 4-7 anni (curva a campana)
int age = (int)GetValueOrDefault(row, "Age");
score += Math.Exp(-0.5 * Math.Pow((age - 5.5) / 2.0, 2)) * 0.05;
// Gender: puledre penalizzate 2%
if (row["Gender"].ToString() == "f")
score -= 0.02;
// 2. HISTORICAL PERFORMANCE (25%)
double careerRuns = GetValueOrDefault(row, "Career Runs");
double careerWins = GetValueOrDefault(row, "Career Wins");
double careerStrike = careerRuns > 0 ? (careerWins / careerRuns) : 0;
score += careerStrike * 0.15;
double placingsRate = GetValueOrDefault(row, "Career Placings") / Math.Max(careerRuns, 1);
score += placingsRate * 0.10;
// 3. RECENT FORM (20%)
int lastPos = (int)GetValueOrDefault(row, "Last Start Finish Position");
score += (lastPos > 0 ? (1.0 / lastPos) : 0) * 0.10;
double lastMargin = (int)GetValueOrDefault(row, "Last Start Margin");
score += (1.0 / (lastMargin + 1)) * 0.05;
//double daysSinceLast = GetDaysSinceLastRace(row); // Da implementare
//score += Math.Exp(-daysSinceLast / 30.0) * 0.05;
// 4. CONDITIONS SPECIALIZATION (15%)
// bool isWet = context.IsWetTrack;
// double surfaceRate = isWet ?
// GetValueOrDefault(row, "Wet Track Strike Rate") :
//// GetValueOrDefault(row, "Dry Track Strike Rate");
// score += surfaceRate * 0.10;
double distanceChange = GetValueOrDefault(row, "Last Start Distance Change");
double distanceFit = 1.0 / (Math.Abs(distanceChange) + 1);
score += distanceFit * 0.05;
// 5. CONTEXT-SPECIFIC FACTORS (10%)
double trackStrike = GetConditionalRate(row, "This Track Wins", "This Track Runs");
score += trackStrike * 0.06;
//double barrierEffect = (1.0 - (GetValueOrDefault(row, "Barrier") / context.MaxBarrier)) * 0.04;
//score += barrierEffect;
// 6. TEAM PERFORMANCE (15%)
double jockeyRecent = GetValueOrDefault(row, "Jockey Last 100 Strike Rate");
double jockeyLong = GetValueOrDefault(row, "Jockey 12 Months Strike Rate");
score += Math.Max(jockeyRecent, jockeyLong) * 0.08;
double trainerRecent = GetValueOrDefault(row, "Trainer Last 100 Strike Rate");
double trainerLong = GetValueOrDefault(row, "Trainer 12 Months Strike Rate");
score += Math.Max(trainerRecent, trainerLong) * 0.07;
// 7. PHYSICAL FACTORS (10%)
// double weightRatio = context.AvgWeight / GetValueOrDefault(row, "Weight Carried");
// score += Math.Min(weightRatio, 1.5) * 0.05;
double prizeMomentum = GetValueOrDefault(row, "Average Prize Money") / GetValueOrDefault(row, "Prize Money");
score += prizeMomentum * 0.05;
return score * 100;
}
/// <summary>
/// Calcola il rating ML per un cavallo utilizzando il modello di machine learning
/// </summary>
public int CalculateMLRating(DataRow row)
{
try
{
// Prepara l'input per il modello ML
var input = new ModelInput
{
Age = GetFloatValueOrDefault(row, "Age"),
HandicapRating = GetFloatValueOrDefault(row, "Handicap Rating"),
Weight = GetFloatValueOrDefault(row, "Weight"),
WeightCarried = GetFloatValueOrDefault(row, "Weight Carried"),
Barrier = GetFloatValueOrDefault(row, "Barrier"),
CareerRuns = GetFloatValueOrDefault(row, "Career Runs"),
CareerWins = GetFloatValueOrDefault(row, "Career Wins"),
CareerStrikeRate = GetFloatValueOrDefault(row, "Career Strike Rate"),
CareerROI = GetFloatValueOrDefault(row, "Career ROI"),
ThisTrackRuns = GetFloatValueOrDefault(row, "This Track Runs"),
ThisTrackWins = GetFloatValueOrDefault(row, "This Track Wins"),
ThisTrackStrikeRate = GetFloatValueOrDefault(row, "This Track Strike Rate"),
ThisDistanceRuns = GetFloatValueOrDefault(row, "This Distance Runs"),
ThisDistanceWins = GetFloatValueOrDefault(row, "This Distance Wins"),
ThisDistanceStrikeRate = GetFloatValueOrDefault(row, "This Distance Strike Rate"),
JockeyLast100Wins = GetFloatValueOrDefault(row, "Jockey Last 100 Wins"),
JockeyLast100StrikeRate = GetFloatValueOrDefault(row, "Jockey Last 100 Strike Rate"),
TrainerLast100Wins = GetFloatValueOrDefault(row, "Trainer Last 100 Wins"),
TrainerLast100StrikeRate = GetFloatValueOrDefault(row, "Trainer Last 100 Strike Rate"),
BestFixedOdds = GetFloatValueOrDefault(row, "Best Fixed Odds")
};
// Effettua la previsione usando il modello ML
var output = _mlService.PredictFinishPosition(input);
// Il rating ML è inversamente proporzionale alla posizione prevista
// Migliore è la posizione prevista (più vicina a 1), più alto sarà il rating
double mlRating = 100 * (1.0 / Math.Max(1, output.PredictedPosition));
// Aggiusta il rating in base alla probabilità di vittoria
mlRating = mlRating * (0.6 + (output.WinProbability * 0.4));
return (int)Math.Min(100, Math.Max(0, mlRating));
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel calcolo del rating ML: {ex.Message}");
// In caso di errore, ritorna un rating basato sul metodo standard
return CalculateRating1(row);
}
}
private double GetValueOrDefault(DataRow row, string columnName)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
return 0;
return Convert.ToDouble(row[columnName]);
}
private float GetFloatValueOrDefault(DataRow row, string columnName)
{
if (!row.Table.Columns.Contains(columnName) || row[columnName] == DBNull.Value)
return 0f;
return Convert.ToSingle(row[columnName]);
}
private double GetConditionalRate(DataRow row, string numeratorColumn, string denominatorColumn)
{
double numerator = GetValueOrDefault(row, numeratorColumn);
double denominator = GetValueOrDefault(row, denominatorColumn);
if (denominator == 0)
return 0;
return numerator / denominator;
}
}
}
@@ -0,0 +1,457 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
using System.IO;
using BettingPredictor;
using HorseRacingPredictor.Manager;
namespace HorseRacingPredictor.Horses
{
internal class Database : HorseRacingPredictor.Manager.Database
{
// Implementazione della proprietà astratta _connectionString per il database cavalli
protected override string _connectionString => "Server=DESKTOP-9O9JHFS;Database=TestBS_Horses;User Id=sa;Password=Asti2019;";
private readonly FileReader fileReaderHorses;
public Database()
{
fileReaderHorses = new FileReader();
}
public DataTable GetAllHorseRaceData()
{
DataTable horseRaceData = new DataTable();
ExecuteQuery("GetAllHorseRaceData", connection =>
{
using (SqlCommand command = new SqlCommand("SELECT * FROM HorseRaceData", connection))
{
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(horseRaceData);
}
}
});
return horseRaceData;
}
public void ProcessAndInsertHorseRaceData(string filePath)
{
try
{
// Leggi i dati dal file CSV usando il nuovo metodo che restituisce HorseRaceData
List<HorseRaceData> horseDataList = fileReaderHorses.ReadHorseDataFromFile(filePath);
// Utilizza i metodi del FileReader per estrarre informazioni sulla corsa
DateTime raceDate = fileReaderHorses.ExtractRaceDate(filePath);
string meeting = fileReaderHorses.ExtractMeetingName(filePath);
int race = fileReaderHorses.ExtractRaceNumber(filePath);
// Inserisci i dati nel database
InsertHorseDataInRacesTable(horseDataList, meeting, race, raceDate, filePath);
Console.WriteLine($"Elaborazione e inserimento dati corsa cavalli completata: {filePath}");
}
catch (Exception ex)
{
LogError($"l'elaborazione del file {filePath}", ex);
}
}
private void InsertHorseDataInRacesTable(List<HorseRaceData> horseDataList, string meeting, int race, DateTime raceDate, string filePath)
{
string operation = "l'inserimento dei dati dei cavalli nella tabella Races";
ExecuteQuery(operation, connection =>
{
foreach (var horseData in horseDataList)
{
UpsertRacesTableData(connection, horseData, meeting, race, raceDate, filePath);
}
});
}
private void UpsertRacesTableData(SqlConnection connection, HorseRaceData horseData, string meeting, int race, DateTime raceDate, string filePath)
{
try
{
// Numero del cavallo
int num = 0;
int.TryParse(horseData.Num, out num);
// Data e ora attuale per i campi Created e Updated
DateTime currentDateTime = DateTime.Now;
string query = @"
IF EXISTS (SELECT 1 FROM Races WHERE Data = @Data AND Meeting = @Meeting AND Race = @Race AND Num = @Num)
BEGIN
UPDATE Races SET
FileName = @FileName,
HorseName = @HorseName,
Age = @Age,
Gender = @Gender,
HandicapRating = @HandicapRating,
CareerRuns = @CareerRuns,
CareerWins = @CareerWins,
CareerStrikeRate = @CareerStrikeRate,
CareerROI = @CareerROI,
CareerPlacings = @CareerPlacings,
CareerPlaceStrikeRate = @CareerPlaceStrikeRate,
DryTrackRuns = @DryTrackRuns,
DryTrackWins = @DryTrackWins,
DryTrackStrikeRate = @DryTrackStrikeRate,
DryTrackROI = @DryTrackROI,
WetTrackRuns = @WetTrackRuns,
WetTrackWins = @WetTrackWins,
WetTrackStrikeRate = @WetTrackStrikeRate,
WetTrackROI = @WetTrackROI,
AveragePrizeMoney = @AveragePrizeMoney,
CareerPrizeMoney = @CareerPrizeMoney,
BestFixedOdds = @BestFixedOdds,
BetEasyOdds = @BetEasyOdds,
Weight = @Weight,
WeightCarried = @WeightCarried,
Barrier = @Barrier,
PrizeMoney = @PrizeMoney,
ThisTrackRuns = @ThisTrackRuns,
ThisTrackWins = @ThisTrackWins,
ThisTrackStrikeRate = @ThisTrackStrikeRate,
ThisTrackROI = @ThisTrackROI,
ThisTrackPlaces = @ThisTrackPlaces,
ThisTrackPlaceStrikeRate = @ThisTrackPlaceStrikeRate,
ThisDistanceRuns = @ThisDistanceRuns,
ThisDistanceWins = @ThisDistanceWins,
ThisDistanceStrikeRate = @ThisDistanceStrikeRate,
ThisDistanceROI = @ThisDistanceROI,
ThisDistancePlaces = @ThisDistancePlaces,
ThisDistancePlaceStrikeRate = @ThisDistancePlaceStrikeRate,
ThisTrackDistanceRuns = @ThisTrackDistanceRuns,
ThisTrackDistanceWins = @ThisTrackDistanceWins,
ThisTrackDistanceStrikeRate = @ThisTrackDistanceStrikeRate,
ThisTrackDistanceROI = @ThisTrackDistanceROI,
ThisTrackDistancePlaces = @ThisTrackDistancePlaces,
ThisTrackDistancePlaceStrikeRate = @ThisTrackDistancePlaceStrikeRate,
ThisConditionRuns = @ThisConditionRuns,
ThisConditionWins = @ThisConditionWins,
ThisConditionStrikeRate = @ThisConditionStrikeRate,
ThisConditionROI = @ThisConditionROI,
ThisConditionPlaces = @ThisConditionPlaces,
ThisConditionPlaceStrikeRate = @ThisConditionPlaceStrikeRate,
Jockey = @Jockey,
Apprentice = @Apprentice,
JockeyWeightClaim = @JockeyWeightClaim,
JockeyLast100HorseEarnings = @JockeyLast100HorseEarnings,
JockeyLast100AvgHorseEarnings = @JockeyLast100AvgHorseEarnings,
JockeyLast100Starts = @JockeyLast100Starts,
JockeyLast100Wins = @JockeyLast100Wins,
JockeyLast100StrikeRate = @JockeyLast100StrikeRate,
JockeyLast100ROI = @JockeyLast100ROI,
JockeyLast100Places = @JockeyLast100Places,
JockeyLast100PlaceStrikeRate = @JockeyLast100PlaceStrikeRate,
Jockey12MonthHorseEarnings = @Jockey12MonthHorseEarnings,
Jockey12MonthAvgHorseEarnings = @Jockey12MonthAvgHorseEarnings,
Jockey12MonthsStarts = @Jockey12MonthsStarts,
Jockey12MonthsWins = @Jockey12MonthsWins,
Jockey12MonthsStrikeRate = @Jockey12MonthsStrikeRate,
Jockey12MonthsROI = @Jockey12MonthsROI,
Jockey12MonthsPlaces = @Jockey12MonthsPlaces,
Jockey12MonthsPlaceStrikeRate = @Jockey12MonthsPlaceStrikeRate,
JockeyThisSeasonHorseEarnings = @JockeyThisSeasonHorseEarnings,
JockeyThisSeasonAvgHorseEarnings = @JockeyThisSeasonAvgHorseEarnings,
JockeyThisSeasonStarts = @JockeyThisSeasonStarts,
JockeyThisSeasonWins = @JockeyThisSeasonWins,
JockeyThisSeasonStrikeRate = @JockeyThisSeasonStrikeRate,
JockeyThisSeasonROI = @JockeyThisSeasonROI,
JockeyThisSeasonPlaces = @JockeyThisSeasonPlaces,
JockeyThisSeasonPlaceStrikeRate = @JockeyThisSeasonPlaceStrikeRate,
JockeyLastSeasonHorseEarnings = @JockeyLastSeasonHorseEarnings,
JockeyLastSeasonAvgHorseEarnings = @JockeyLastSeasonAvgHorseEarnings,
JockeyLastSeasonStarts = @JockeyLastSeasonStarts,
JockeyLastSeasonWins = @JockeyLastSeasonWins,
JockeyLastSeasonStrikeRate = @JockeyLastSeasonStrikeRate,
JockeyLastSeasonROI = @JockeyLastSeasonROI,
JockeyLastSeasonPlaces = @JockeyLastSeasonPlaces,
JockeyLastSeasonPlaceStrikeRate = @JockeyLastSeasonPlaceStrikeRate,
Trainer = @Trainer,
TrainerLast100HorseEarnings = @TrainerLast100HorseEarnings,
TrainerLast100AvgHorseEarnings = @TrainerLast100AvgHorseEarnings,
TrainerLast100Starts = @TrainerLast100Starts,
TrainerLast100Wins = @TrainerLast100Wins,
TrainerLast100StrikeRate = @TrainerLast100StrikeRate,
TrainerLast100ROI = @TrainerLast100ROI,
TrainerLast100Places = @TrainerLast100Places,
TrainerLast100PlaceStrikeRate = @TrainerLast100PlaceStrikeRate,
Trainer12MonthHorseEarnings = @Trainer12MonthHorseEarnings,
Trainer12MonthAvgHorseEarnings = @Trainer12MonthAvgHorseEarnings,
Trainer12MonthsStarts = @Trainer12MonthsStarts,
Trainer12MonthsWins = @Trainer12MonthsWins,
Trainer12MonthsStrikeRate = @Trainer12MonthsStrikeRate,
Trainer12MonthsROI = @Trainer12MonthsROI,
Trainer12MonthsPlaces = @Trainer12MonthsPlaces,
Trainer12MonthsPlaceStrikeRate = @Trainer12MonthsPlaceStrikeRate,
TrainerThisSeasonHorseEarnings = @TrainerThisSeasonHorseEarnings,
TrainerThisSeasonAvgHorseEarnings = @TrainerThisSeasonAvgHorseEarnings,
TrainerThisSeasonStarts = @TrainerThisSeasonStarts,
TrainerThisSeasonWins = @TrainerThisSeasonWins,
TrainerThisSeasonStrikeRate = @TrainerThisSeasonStrikeRate,
TrainerThisSeasonROI = @TrainerThisSeasonROI,
TrainerThisSeasonPlaces = @TrainerThisSeasonPlaces,
TrainerThisSeasonPlaceStrikeRate = @TrainerThisSeasonPlaceStrikeRate,
TrainerLastSeasonHorseEarnings = @TrainerLastSeasonHorseEarnings,
TrainerLastSeasonAvgHorseEarnings = @TrainerLastSeasonAvgHorseEarnings,
TrainerLastSeasonStarts = @TrainerLastSeasonStarts,
TrainerLastSeasonWins = @TrainerLastSeasonWins,
TrainerLastSeasonStrikeRate = @TrainerLastSeasonStrikeRate,
TrainerLastSeasonROI = @TrainerLastSeasonROI,
TrainerLastSeasonPlaces = @TrainerLastSeasonPlaces,
TrainerLastSeasonPlaceStrikeRate = @TrainerLastSeasonPlaceStrikeRate,
LastStartFinishPosition = @LastStartFinishPosition,
LastStartMargin = @LastStartMargin,
LastStartDistance = @LastStartDistance,
LastStartDistanceChange = @LastStartDistanceChange,
LastStartPrizeMoney = @LastStartPrizeMoney,
FormGuideUrl = @FormGuideUrl,
HorseProfileUrl = @HorseProfileUrl,
JockeyProfileUrl = @JockeyProfileUrl,
TrainerProfileUrl = @TrainerProfileUrl,
FinishResult = @FinishResult,
Updated = @Updated
WHERE Data = @Data AND Meeting = @Meeting AND Race = @Race AND Num = @Num
END
ELSE
BEGIN
INSERT INTO Races (
Data, Meeting, Race, Num, FileName, HorseName, Age, Gender, HandicapRating,
CareerRuns, CareerWins, CareerStrikeRate, CareerROI, CareerPlacings, CareerPlaceStrikeRate,
DryTrackRuns, DryTrackWins, DryTrackStrikeRate, DryTrackROI, WetTrackRuns, WetTrackWins,
WetTrackStrikeRate, WetTrackROI, AveragePrizeMoney, CareerPrizeMoney, BestFixedOdds, BetEasyOdds,
Weight, WeightCarried, Barrier, PrizeMoney, ThisTrackRuns, ThisTrackWins, ThisTrackStrikeRate,
ThisTrackROI, ThisTrackPlaces, ThisTrackPlaceStrikeRate, ThisDistanceRuns, ThisDistanceWins,
ThisDistanceStrikeRate, ThisDistanceROI, ThisDistancePlaces, ThisDistancePlaceStrikeRate,
ThisTrackDistanceRuns, ThisTrackDistanceWins, ThisTrackDistanceStrikeRate, ThisTrackDistanceROI,
ThisTrackDistancePlaces, ThisTrackDistancePlaceStrikeRate, ThisConditionRuns, ThisConditionWins,
ThisConditionStrikeRate, ThisConditionROI, ThisConditionPlaces, ThisConditionPlaceStrikeRate,
Jockey, Apprentice, JockeyWeightClaim, JockeyLast100HorseEarnings, JockeyLast100AvgHorseEarnings,
JockeyLast100Starts, JockeyLast100Wins, JockeyLast100StrikeRate, JockeyLast100ROI, JockeyLast100Places,
JockeyLast100PlaceStrikeRate, Jockey12MonthHorseEarnings, Jockey12MonthAvgHorseEarnings,
Jockey12MonthsStarts, Jockey12MonthsWins, Jockey12MonthsStrikeRate, Jockey12MonthsROI,
Jockey12MonthsPlaces, Jockey12MonthsPlaceStrikeRate, JockeyThisSeasonHorseEarnings,
JockeyThisSeasonAvgHorseEarnings, JockeyThisSeasonStarts, JockeyThisSeasonWins,
JockeyThisSeasonStrikeRate, JockeyThisSeasonROI, JockeyThisSeasonPlaces, JockeyThisSeasonPlaceStrikeRate,
JockeyLastSeasonHorseEarnings, JockeyLastSeasonAvgHorseEarnings, JockeyLastSeasonStarts,
JockeyLastSeasonWins, JockeyLastSeasonStrikeRate, JockeyLastSeasonROI, JockeyLastSeasonPlaces,
JockeyLastSeasonPlaceStrikeRate, Trainer, TrainerLast100HorseEarnings, TrainerLast100AvgHorseEarnings,
TrainerLast100Starts, TrainerLast100Wins, TrainerLast100StrikeRate, TrainerLast100ROI,
TrainerLast100Places, TrainerLast100PlaceStrikeRate, Trainer12MonthHorseEarnings,
Trainer12MonthAvgHorseEarnings, Trainer12MonthsStarts, Trainer12MonthsWins, Trainer12MonthsStrikeRate,
Trainer12MonthsROI, Trainer12MonthsPlaces, Trainer12MonthsPlaceStrikeRate, TrainerThisSeasonHorseEarnings,
TrainerThisSeasonAvgHorseEarnings, TrainerThisSeasonStarts, TrainerThisSeasonWins,
TrainerThisSeasonStrikeRate, TrainerThisSeasonROI, TrainerThisSeasonPlaces, TrainerThisSeasonPlaceStrikeRate,
TrainerLastSeasonHorseEarnings, TrainerLastSeasonAvgHorseEarnings, TrainerLastSeasonStarts,
TrainerLastSeasonWins, TrainerLastSeasonStrikeRate, TrainerLastSeasonROI, TrainerLastSeasonPlaces,
TrainerLastSeasonPlaceStrikeRate, LastStartFinishPosition, LastStartMargin, LastStartDistance,
LastStartDistanceChange, LastStartPrizeMoney, FormGuideUrl, HorseProfileUrl, JockeyProfileUrl,
TrainerProfileUrl, FinishResult, Created, Updated
) VALUES (
@Data, @Meeting, @Race, @Num, @FileName, @HorseName, @Age, @Gender, @HandicapRating,
@CareerRuns, @CareerWins, @CareerStrikeRate, @CareerROI, @CareerPlacings, @CareerPlaceStrikeRate,
@DryTrackRuns, @DryTrackWins, @DryTrackStrikeRate, @DryTrackROI, @WetTrackRuns, @WetTrackWins,
@WetTrackStrikeRate, @WetTrackROI, @AveragePrizeMoney, @CareerPrizeMoney, @BestFixedOdds, @BetEasyOdds,
@Weight, @WeightCarried, @Barrier, @PrizeMoney, @ThisTrackRuns, @ThisTrackWins, @ThisTrackStrikeRate,
@ThisTrackROI, @ThisTrackPlaces, @ThisTrackPlaceStrikeRate, @ThisDistanceRuns, @ThisDistanceWins,
@ThisDistanceStrikeRate, @ThisDistanceROI, @ThisDistancePlaces, @ThisDistancePlaceStrikeRate,
@ThisTrackDistanceRuns, @ThisTrackDistanceWins, @ThisTrackDistanceStrikeRate, @ThisTrackDistanceROI,
@ThisTrackDistancePlaces, @ThisTrackDistancePlaceStrikeRate, @ThisConditionRuns, @ThisConditionWins,
@ThisConditionStrikeRate, @ThisConditionROI, @ThisConditionPlaces, @ThisConditionPlaceStrikeRate,
@Jockey, @Apprentice, @JockeyWeightClaim, @JockeyLast100HorseEarnings, @JockeyLast100AvgHorseEarnings,
@JockeyLast100Starts, @JockeyLast100Wins, @JockeyLast100StrikeRate, @JockeyLast100ROI, @JockeyLast100Places,
@JockeyLast100PlaceStrikeRate, @Jockey12MonthHorseEarnings, @Jockey12MonthAvgHorseEarnings,
@Jockey12MonthsStarts, @Jockey12MonthsWins, @Jockey12MonthsStrikeRate, @Jockey12MonthsROI,
@Jockey12MonthsPlaces, @Jockey12MonthsPlaceStrikeRate, @JockeyThisSeasonHorseEarnings,
@JockeyThisSeasonAvgHorseEarnings, @JockeyThisSeasonStarts, @JockeyThisSeasonWins,
@JockeyThisSeasonStrikeRate, @JockeyThisSeasonROI, @JockeyThisSeasonPlaces, @JockeyThisSeasonPlaceStrikeRate,
@JockeyLastSeasonHorseEarnings, @JockeyLastSeasonAvgHorseEarnings, @JockeyLastSeasonStarts,
@JockeyLastSeasonWins, @JockeyLastSeasonStrikeRate, @JockeyLastSeasonROI, @JockeyLastSeasonPlaces,
@JockeyLastSeasonPlaceStrikeRate, @Trainer, @TrainerLast100HorseEarnings, @TrainerLast100AvgHorseEarnings,
@TrainerLast100Starts, @TrainerLast100Wins, @TrainerLast100StrikeRate, @TrainerLast100ROI,
@TrainerLast100Places, @TrainerLast100PlaceStrikeRate, @Trainer12MonthHorseEarnings,
@Trainer12MonthAvgHorseEarnings, @Trainer12MonthsStarts, @Trainer12MonthsWins, @Trainer12MonthsStrikeRate,
@Trainer12MonthsROI, @Trainer12MonthsPlaces, @Trainer12MonthsPlaceStrikeRate, @TrainerThisSeasonHorseEarnings,
@TrainerThisSeasonAvgHorseEarnings, @TrainerThisSeasonStarts, @TrainerThisSeasonWins,
@TrainerThisSeasonStrikeRate, @TrainerThisSeasonROI, @TrainerThisSeasonPlaces, @TrainerThisSeasonPlaceStrikeRate,
@TrainerLastSeasonHorseEarnings, @TrainerLastSeasonAvgHorseEarnings, @TrainerLastSeasonStarts,
@TrainerLastSeasonWins, @TrainerLastSeasonStrikeRate, @TrainerLastSeasonROI, @TrainerLastSeasonPlaces,
@TrainerLastSeasonPlaceStrikeRate, @LastStartFinishPosition, @LastStartMargin, @LastStartDistance,
@LastStartDistanceChange, @LastStartPrizeMoney, @FormGuideUrl, @HorseProfileUrl, @JockeyProfileUrl,
@TrainerProfileUrl, @FinishResult, @Created, @Updated
)
END";
using (var command = new SqlCommand(query, connection))
{
// Chiavi primarie
command.Parameters.AddWithValue("@Data", raceDate);
command.Parameters.AddWithValue("@Meeting", meeting ?? string.Empty);
command.Parameters.AddWithValue("@Race", race);
command.Parameters.AddWithValue("@Num", num);
// Dati accessori
command.Parameters.AddWithValue("@FileName", filePath ?? string.Empty);
// Dati cavallo
command.Parameters.AddWithValue("@HorseName", horseData.HorseName ?? string.Empty);
command.Parameters.AddWithValue("@Age", horseData.Age);
command.Parameters.AddWithValue("@Gender", horseData.Gender ?? string.Empty);
command.Parameters.AddWithValue("@HandicapRating", horseData.HandicapRating);
command.Parameters.AddWithValue("@CareerRuns", horseData.CareerRuns);
command.Parameters.AddWithValue("@CareerWins", horseData.CareerWins);
command.Parameters.AddWithValue("@CareerStrikeRate", horseData.CareerStrikeRate);
command.Parameters.AddWithValue("@CareerROI", horseData.CareerROI);
command.Parameters.AddWithValue("@CareerPlacings", horseData.CareerPlacings);
command.Parameters.AddWithValue("@CareerPlaceStrikeRate", horseData.CareerPlaceStrikeRate);
command.Parameters.AddWithValue("@DryTrackRuns", horseData.DryTrackRuns);
command.Parameters.AddWithValue("@DryTrackWins", horseData.DryTrackWins);
command.Parameters.AddWithValue("@DryTrackStrikeRate", horseData.DryTrackStrikeRate);
command.Parameters.AddWithValue("@DryTrackROI", horseData.DryTrackROI);
command.Parameters.AddWithValue("@WetTrackRuns", horseData.WetTrackRuns);
command.Parameters.AddWithValue("@WetTrackWins", horseData.WetTrackWins);
command.Parameters.AddWithValue("@WetTrackStrikeRate", horseData.WetTrackStrikeRate);
command.Parameters.AddWithValue("@WetTrackROI", horseData.WetTrackROI);
command.Parameters.AddWithValue("@AveragePrizeMoney", horseData.AveragePrizeMoney);
command.Parameters.AddWithValue("@CareerPrizeMoney", horseData.CareerPrizeMoney);
command.Parameters.AddWithValue("@BestFixedOdds", horseData.BestFixedOdds);
command.Parameters.AddWithValue("@BetEasyOdds", horseData.BetEasyOdds);
command.Parameters.AddWithValue("@Weight", horseData.Weight);
command.Parameters.AddWithValue("@WeightCarried", horseData.WeightCarried);
command.Parameters.AddWithValue("@Barrier", horseData.Barrier);
command.Parameters.AddWithValue("@PrizeMoney", horseData.PrizeMoney);
// Statistiche pista
command.Parameters.AddWithValue("@ThisTrackRuns", horseData.ThisTrackRuns);
command.Parameters.AddWithValue("@ThisTrackWins", horseData.ThisTrackWins);
command.Parameters.AddWithValue("@ThisTrackStrikeRate", horseData.ThisTrackStrikeRate);
command.Parameters.AddWithValue("@ThisTrackROI", horseData.ThisTrackROI);
command.Parameters.AddWithValue("@ThisTrackPlaces", horseData.ThisTrackPlaces);
command.Parameters.AddWithValue("@ThisTrackPlaceStrikeRate", horseData.ThisTrackPlaceStrikeRate);
command.Parameters.AddWithValue("@ThisDistanceRuns", horseData.ThisDistanceRuns);
command.Parameters.AddWithValue("@ThisDistanceWins", horseData.ThisDistanceWins);
command.Parameters.AddWithValue("@ThisDistanceStrikeRate", horseData.ThisDistanceStrikeRate);
command.Parameters.AddWithValue("@ThisDistanceROI", horseData.ThisDistanceROI);
command.Parameters.AddWithValue("@ThisDistancePlaces", horseData.ThisDistancePlaces);
command.Parameters.AddWithValue("@ThisDistancePlaceStrikeRate", horseData.ThisDistancePlaceStrikeRate);
command.Parameters.AddWithValue("@ThisTrackDistanceRuns", horseData.ThisTrackDistanceRuns);
command.Parameters.AddWithValue("@ThisTrackDistanceWins", horseData.ThisTrackDistanceWins);
command.Parameters.AddWithValue("@ThisTrackDistanceStrikeRate", horseData.ThisTrackDistanceStrikeRate);
command.Parameters.AddWithValue("@ThisTrackDistanceROI", horseData.ThisTrackDistanceROI);
command.Parameters.AddWithValue("@ThisTrackDistancePlaces", horseData.ThisTrackDistancePlaces);
command.Parameters.AddWithValue("@ThisTrackDistancePlaceStrikeRate", horseData.ThisTrackDistancePlaceStrikeRate);
command.Parameters.AddWithValue("@ThisConditionRuns", horseData.ThisConditionRuns);
command.Parameters.AddWithValue("@ThisConditionWins", horseData.ThisConditionWins);
command.Parameters.AddWithValue("@ThisConditionStrikeRate", horseData.ThisConditionStrikeRate);
command.Parameters.AddWithValue("@ThisConditionROI", horseData.ThisConditionROI);
command.Parameters.AddWithValue("@ThisConditionPlaces", horseData.ThisConditionPlaces);
command.Parameters.AddWithValue("@ThisConditionPlaceStrikeRate", horseData.ThisConditionPlaceStrikeRate);
// Jockey
command.Parameters.AddWithValue("@Jockey", horseData.Jockey ?? string.Empty);
command.Parameters.AddWithValue("@Apprentice", horseData.Apprentice ?? string.Empty);
command.Parameters.AddWithValue("@JockeyWeightClaim", horseData.JockeyWeightClaim);
command.Parameters.AddWithValue("@JockeyLast100HorseEarnings", horseData.JockeyLast100HorseEarnings);
command.Parameters.AddWithValue("@JockeyLast100AvgHorseEarnings", horseData.JockeyLast100AvgHorseEarnings);
command.Parameters.AddWithValue("@JockeyLast100Starts", horseData.JockeyLast100Starts);
command.Parameters.AddWithValue("@JockeyLast100Wins", horseData.JockeyLast100Wins);
command.Parameters.AddWithValue("@JockeyLast100StrikeRate", horseData.JockeyLast100StrikeRate);
command.Parameters.AddWithValue("@JockeyLast100ROI", horseData.JockeyLast100ROI);
command.Parameters.AddWithValue("@JockeyLast100Places", horseData.JockeyLast100Places);
command.Parameters.AddWithValue("@JockeyLast100PlaceStrikeRate", horseData.JockeyLast100PlaceStrikeRate);
command.Parameters.AddWithValue("@Jockey12MonthHorseEarnings", horseData.Jockey12MonthHorseEarnings);
command.Parameters.AddWithValue("@Jockey12MonthAvgHorseEarnings", horseData.Jockey12MonthAvgHorseEarnings);
command.Parameters.AddWithValue("@Jockey12MonthsStarts", horseData.Jockey12MonthsStarts);
command.Parameters.AddWithValue("@Jockey12MonthsWins", horseData.Jockey12MonthsWins);
command.Parameters.AddWithValue("@Jockey12MonthsStrikeRate", horseData.Jockey12MonthsStrikeRate);
command.Parameters.AddWithValue("@Jockey12MonthsROI", horseData.Jockey12MonthsROI);
command.Parameters.AddWithValue("@Jockey12MonthsPlaces", horseData.Jockey12MonthsPlaces);
command.Parameters.AddWithValue("@Jockey12MonthsPlaceStrikeRate", horseData.Jockey12MonthsPlaceStrikeRate);
command.Parameters.AddWithValue("@JockeyThisSeasonHorseEarnings", horseData.JockeyThisSeasonHorseEarnings);
command.Parameters.AddWithValue("@JockeyThisSeasonAvgHorseEarnings", horseData.JockeyThisSeasonAvgHorseEarnings);
command.Parameters.AddWithValue("@JockeyThisSeasonStarts", horseData.JockeyThisSeasonStarts);
command.Parameters.AddWithValue("@JockeyThisSeasonWins", horseData.JockeyThisSeasonWins);
command.Parameters.AddWithValue("@JockeyThisSeasonStrikeRate", horseData.JockeyThisSeasonStrikeRate);
command.Parameters.AddWithValue("@JockeyThisSeasonROI", horseData.JockeyThisSeasonROI);
command.Parameters.AddWithValue("@JockeyThisSeasonPlaces", horseData.JockeyThisSeasonPlaces);
command.Parameters.AddWithValue("@JockeyThisSeasonPlaceStrikeRate", horseData.JockeyThisSeasonPlaceStrikeRate);
command.Parameters.AddWithValue("@JockeyLastSeasonHorseEarnings", horseData.JockeyLastSeasonHorseEarnings);
command.Parameters.AddWithValue("@JockeyLastSeasonAvgHorseEarnings", horseData.JockeyLastSeasonAvgHorseEarnings);
command.Parameters.AddWithValue("@JockeyLastSeasonStarts", horseData.JockeyLastSeasonStarts);
command.Parameters.AddWithValue("@JockeyLastSeasonWins", horseData.JockeyLastSeasonWins);
command.Parameters.AddWithValue("@JockeyLastSeasonStrikeRate", horseData.JockeyLastSeasonStrikeRate);
command.Parameters.AddWithValue("@JockeyLastSeasonROI", horseData.JockeyLastSeasonROI);
command.Parameters.AddWithValue("@JockeyLastSeasonPlaces", horseData.JockeyLastSeasonPlaces);
command.Parameters.AddWithValue("@JockeyLastSeasonPlaceStrikeRate", horseData.JockeyLastSeasonPlaceStrikeRate);
// Trainer
command.Parameters.AddWithValue("@Trainer", horseData.Trainer ?? string.Empty);
command.Parameters.AddWithValue("@TrainerLast100HorseEarnings", horseData.TrainerLast100HorseEarnings);
command.Parameters.AddWithValue("@TrainerLast100AvgHorseEarnings", horseData.TrainerLast100AvgHorseEarnings);
command.Parameters.AddWithValue("@TrainerLast100Starts", horseData.TrainerLast100Starts);
command.Parameters.AddWithValue("@TrainerLast100Wins", horseData.TrainerLast100Wins);
command.Parameters.AddWithValue("@TrainerLast100StrikeRate", horseData.TrainerLast100StrikeRate);
command.Parameters.AddWithValue("@TrainerLast100ROI", horseData.TrainerLast100ROI);
command.Parameters.AddWithValue("@TrainerLast100Places", horseData.TrainerLast100Places);
command.Parameters.AddWithValue("@TrainerLast100PlaceStrikeRate", horseData.TrainerLast100PlaceStrikeRate);
command.Parameters.AddWithValue("@Trainer12MonthHorseEarnings", horseData.Trainer12MonthHorseEarnings);
command.Parameters.AddWithValue("@Trainer12MonthAvgHorseEarnings", horseData.Trainer12MonthAvgHorseEarnings);
command.Parameters.AddWithValue("@Trainer12MonthsStarts", horseData.Trainer12MonthsStarts);
command.Parameters.AddWithValue("@Trainer12MonthsWins", horseData.Trainer12MonthsWins);
command.Parameters.AddWithValue("@Trainer12MonthsStrikeRate", horseData.Trainer12MonthsStrikeRate);
command.Parameters.AddWithValue("@Trainer12MonthsROI", horseData.Trainer12MonthsROI);
command.Parameters.AddWithValue("@Trainer12MonthsPlaces", horseData.Trainer12MonthsPlaces);
command.Parameters.AddWithValue("@Trainer12MonthsPlaceStrikeRate", horseData.Trainer12MonthsPlaceStrikeRate);
command.Parameters.AddWithValue("@TrainerThisSeasonHorseEarnings", horseData.TrainerThisSeasonHorseEarnings);
command.Parameters.AddWithValue("@TrainerThisSeasonAvgHorseEarnings", horseData.TrainerThisSeasonAvgHorseEarnings);
command.Parameters.AddWithValue("@TrainerThisSeasonStarts", horseData.TrainerThisSeasonStarts);
command.Parameters.AddWithValue("@TrainerThisSeasonWins", horseData.TrainerThisSeasonWins);
command.Parameters.AddWithValue("@TrainerThisSeasonStrikeRate", horseData.TrainerThisSeasonStrikeRate);
command.Parameters.AddWithValue("@TrainerThisSeasonROI", horseData.TrainerThisSeasonROI);
command.Parameters.AddWithValue("@TrainerThisSeasonPlaces", horseData.TrainerThisSeasonPlaces);
command.Parameters.AddWithValue("@TrainerThisSeasonPlaceStrikeRate", horseData.TrainerThisSeasonPlaceStrikeRate);
command.Parameters.AddWithValue("@TrainerLastSeasonHorseEarnings", horseData.TrainerLastSeasonHorseEarnings);
command.Parameters.AddWithValue("@TrainerLastSeasonAvgHorseEarnings", horseData.TrainerLastSeasonAvgHorseEarnings);
command.Parameters.AddWithValue("@TrainerLastSeasonStarts", horseData.TrainerLastSeasonStarts);
command.Parameters.AddWithValue("@TrainerLastSeasonWins", horseData.TrainerLastSeasonWins);
command.Parameters.AddWithValue("@TrainerLastSeasonStrikeRate", horseData.TrainerLastSeasonStrikeRate);
command.Parameters.AddWithValue("@TrainerLastSeasonROI", horseData.TrainerLastSeasonROI);
command.Parameters.AddWithValue("@TrainerLastSeasonPlaces", horseData.TrainerLastSeasonPlaces);
command.Parameters.AddWithValue("@TrainerLastSeasonPlaceStrikeRate", horseData.TrainerLastSeasonPlaceStrikeRate);
// Informazioni ultima corsa
command.Parameters.AddWithValue("@LastStartFinishPosition", horseData.LastStartFinishPosition);
command.Parameters.AddWithValue("@LastStartMargin", horseData.LastStartMargin);
command.Parameters.AddWithValue("@LastStartDistance", horseData.LastStartDistance);
command.Parameters.AddWithValue("@LastStartDistanceChange", horseData.LastStartDistanceChange);
command.Parameters.AddWithValue("@LastStartPrizeMoney", horseData.LastStartPrizeMoney);
// URL e link
command.Parameters.AddWithValue("@FormGuideUrl", horseData.FormGuideUrl ?? string.Empty);
command.Parameters.AddWithValue("@HorseProfileUrl", horseData.HorseProfileUrl ?? string.Empty);
command.Parameters.AddWithValue("@JockeyProfileUrl", horseData.JockeyProfileUrl ?? string.Empty);
command.Parameters.AddWithValue("@TrainerProfileUrl", horseData.TrainerProfileUrl ?? string.Empty);
// Risultato finale
command.Parameters.AddWithValue("@FinishResult", horseData.FinishResult ?? string.Empty);
// Campi per timestamp
command.Parameters.AddWithValue("@Created", currentDateTime);
command.Parameters.AddWithValue("@Updated", currentDateTime);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError($"l'upsert dei dati del cavallo {horseData.HorseName}", ex);
}
}
}
}
@@ -0,0 +1,250 @@
using CsvHelper;
using CsvHelper.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace HorseRacingPredictor.Horses
{
/// <summary>
/// Gestore per la lettura e l'elaborazione dei file CSV relativi alle corse dei cavalli
/// </summary>
public class FileReader : HorseRacingPredictor.Manager.FileReader
{
private readonly Manager.FileReader _fileReader;
/// <summary>
/// Costruttore della classe FileReader
/// </summary>
public FileReader()
{
_fileReader = new Manager.FileReader();
}
/// <summary>
/// Estrae il nome del meeting dal nome del file
/// </summary>
/// <param name="filePath">Percorso del file</param>
/// <returns>Nome del meeting</returns>
public string ExtractMeetingName(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
}
// Ottiene solo il nome del file senza percorso
string fileName = Path.GetFileName(filePath);
// Cerca il pattern YYYYMMDD-meetingname-rXX.csv
var regex = new Regex(@"^\d{8}-([a-zA-Z-]+)-r\d+\.csv$", RegexOptions.IgnoreCase);
var match = regex.Match(fileName);
if (match.Success && match.Groups.Count > 1)
{
// Estrae il nome del meeting e lo formatta
string meetingName = match.Groups[1].Value;
// Sostituisce i trattini con spazi e formatta ogni parola con l'iniziale maiuscola
meetingName = string.Join(" ", meetingName.Split('-')
.Select(s => char.ToUpper(s[0]) + s.Substring(1).ToLower()));
return meetingName;
}
// Se il pattern non corrisponde, restituisce il nome del file senza estensione
return Path.GetFileNameWithoutExtension(filePath);
}
/// <summary>
/// Estrae il numero della corsa dal nome del file
/// </summary>
/// <param name="filePath">Percorso del file</param>
/// <returns>Numero della corsa o -1 se non può essere determinato</returns>
public int ExtractRaceNumber(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
}
// Ottiene solo il nome del file senza percorso
string fileName = Path.GetFileName(filePath);
// Cerca il pattern YYYYMMDD-meetingname-rXX.csv dove XX è il numero della corsa
var regex = new Regex(@"^\d{8}-[a-zA-Z-]+-r(\d+)\.csv$", RegexOptions.IgnoreCase);
var match = regex.Match(fileName);
if (match.Success && match.Groups.Count > 1)
{
// Estrae il numero della corsa
if (int.TryParse(match.Groups[1].Value, out int raceNumber))
{
return raceNumber;
}
}
return -1; // Restituisce -1 se non è possibile determinare il numero della corsa
}
/// <summary>
/// Estrae la data della corsa dal nome del file
/// </summary>
/// <param name="filePath">Percorso del file</param>
/// <returns>Data della corsa o DateTime.MinValue se non può essere determinata</returns>
public DateTime ExtractRaceDate(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentException("Il percorso del file non può essere vuoto.", nameof(filePath));
}
// Ottiene solo il nome del file senza percorso
string fileName = Path.GetFileName(filePath);
// Cerca il pattern YYYYMMDD all'inizio del nome file
var regex = new Regex(@"^(\d{4})(\d{2})(\d{2})-", RegexOptions.IgnoreCase);
var match = regex.Match(fileName);
if (match.Success && match.Groups.Count > 3)
{
// Estrae anno, mese e giorno
if (int.TryParse(match.Groups[1].Value, out int year) &&
int.TryParse(match.Groups[2].Value, out int month) &&
int.TryParse(match.Groups[3].Value, out int day))
{
try
{
return new DateTime(year, month, day);
}
catch (ArgumentOutOfRangeException)
{
// Data non valida
return DateTime.MinValue;
}
}
}
return DateTime.MinValue; // Restituisce DateTime.MinValue se non è possibile determinare la data
}
/// <summary>
/// Legge i dati dei cavalli da un file CSV utilizzando CsvHelper
/// </summary>
/// <param name="filePath">Percorso del file CSV</param>
/// <returns>Lista di oggetti HorseRaceData</returns>
public List<HorseRaceData> ReadHorseDataFromFile(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}");
}
try
{
// Configurazione per CsvHelper che utilizza i convertitori centralizzati
var myConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ",",
HasHeaderRecord = true,
HeaderValidated = null,
MissingFieldFound = null,
BadDataFound = null
};
using (var reader = new StreamReader(filePath))
using (var csv = new CsvReader(reader, myConfig))
{
// Utilizzo dei convertitori centralizzati
csv.Context.TypeConverterCache.AddConverter<decimal?>(new NullableDecimalConverter());
csv.Context.TypeConverterCache.AddConverter<int?>(new NullableIntConverter());
// Registriamo la mappa di conversione tra CSV e modello
csv.Context.RegisterClassMap<Punters>();
// Leggi i record
var records = csv.GetRecords<HorseRaceData>().ToList();
// Stampa il numero di record letti
Console.WriteLine($"Letti {records.Count} record dal file {Path.GetFileName(filePath)}");
return records;
}
}
catch (Exception ex)
{
Console.WriteLine($"Errore durante la lettura del file CSV {filePath}: {ex.Message}");
throw;
}
}
/// <summary>
/// Ottiene tutti i file CSV relativi alle corse dei cavalli in una cartella
/// </summary>
/// <param name="folderPath">Percorso della cartella</param>
/// <returns>Lista di percorsi dei file CSV</returns>
public List<string> GetHorseRaceFiles(string folderPath)
{
return _fileReader.GetCsvFiles(folderPath);
}
/// <summary>
/// Ottiene informazioni sulla corsa dal nome del file
/// </summary>
/// <param name="filePath">Percorso del file</param>
/// <returns>Tupla contenente (NomeMeeting, NumeroCorsa, DataCorsa)</returns>
public (string MeetingName, int RaceNumber, DateTime RaceDate) GetRaceInfoFromFileName(string filePath)
{
string meetingName = ExtractMeetingName(filePath);
int raceNumber = ExtractRaceNumber(filePath);
DateTime raceDate = ExtractRaceDate(filePath);
return (meetingName, raceNumber, raceDate);
}
/// <summary>
/// Metodo per processare tutti i file di corse in una cartella
/// </summary>
/// <param name="folderPath">Percorso della cartella contenente i file CSV</param>
/// <returns>Dictionary con chiave = identificativo corsa, valore = lista di dati cavalli</returns>
public Dictionary<string, List<HorseRaceData>> ProcessAllRaceFiles(string folderPath)
{
var result = new Dictionary<string, List<HorseRaceData>>();
var files = GetHorseRaceFiles(folderPath);
Console.WriteLine($"Trovati {files.Count} file CSV nella cartella {folderPath}");
foreach (var file in files)
{
try
{
Console.WriteLine($"Elaborazione file: {Path.GetFileName(file)}");
var horseDataList = ReadHorseDataFromFile(file);
var raceInfo = GetRaceInfoFromFileName(file);
string key = $"{raceInfo.RaceDate:yyyy-MM-dd} - {raceInfo.MeetingName} - Race {raceInfo.RaceNumber}";
result.Add(key, horseDataList);
Console.WriteLine($"Elaborati {horseDataList.Count} record per la corsa {key}");
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel processare il file {file}: {ex.Message}");
}
}
Console.WriteLine($"Totale corse elaborate: {result.Count}");
return result;
}
}
}
@@ -0,0 +1,186 @@
using CsvHelper.Configuration;
using System;
namespace HorseRacingPredictor.Horses
{
public sealed class Punters : ClassMap<HorseRaceData>
{
public Punters()
{
// Mappatura informazioni principali sul cavallo
Map(m => m.Num).Name("Num", "#Num", "Number");
Map(m => m.HorseName).Name("Horse Name", "HorseName", "Name");
Map(m => m.Age).Name("Age");
Map(m => m.Gender).Name("Gender");
Map(m => m.HandicapRating).Name("Handicap Rating");
// Statistiche di carriera
Map(m => m.CareerRuns).Name("Career Runs");
Map(m => m.CareerWins).Name("Career Wins");
Map(m => m.CareerStrikeRate).Name("Career Strike Rate");
Map(m => m.CareerROI).Name("Career ROI");
Map(m => m.CareerPlacings).Name("Career Placings");
Map(m => m.CareerPlaceStrikeRate).Name("Career Place Strike Rate");
// Statistiche su pista secca
Map(m => m.DryTrackRuns).Name("Dry Track Runs");
Map(m => m.DryTrackWins).Name("Dry Track Wins");
Map(m => m.DryTrackStrikeRate).Name("Dry Track Strike Rate");
Map(m => m.DryTrackROI).Name("Dry Track ROI");
// Statistiche su pista bagnata
Map(m => m.WetTrackRuns).Name("Wet Track Runs");
Map(m => m.WetTrackWins).Name("Wet Track Wins");
Map(m => m.WetTrackStrikeRate).Name("Wet Track Strike Rate");
Map(m => m.WetTrackROI).Name("Wet Track ROI");
// Statistiche sui premi
Map(m => m.AveragePrizeMoney).Name("Average Prize Money");
Map(m => m.CareerPrizeMoney).Name("Career Prize Money");
// Quote e pesi
Map(m => m.BestFixedOdds).Name("Best Fixed Odds");
Map(m => m.BetEasyOdds).Name("BetEasy Odds");
Map(m => m.Weight).Name("Weight");
Map(m => m.WeightCarried).Name("Weight Carried");
Map(m => m.Barrier).Name("Barrier");
Map(m => m.PrizeMoney).Name("Prize Money");
// Statistiche sulla pista attuale
Map(m => m.ThisTrackRuns).Name("This Track Runs");
Map(m => m.ThisTrackWins).Name("This Track Wins");
Map(m => m.ThisTrackStrikeRate).Name("This Track Strike Rate");
Map(m => m.ThisTrackROI).Name("This Track ROI");
Map(m => m.ThisTrackPlaces).Name("This Track Places");
Map(m => m.ThisTrackPlaceStrikeRate).Name("This Track Place Strike Rate");
// Statistiche sulla distanza
Map(m => m.ThisDistanceRuns).Name("This Distance Runs");
Map(m => m.ThisDistanceWins).Name("This Distance Wins");
Map(m => m.ThisDistanceStrikeRate).Name("This Distance Strike Rate");
Map(m => m.ThisDistanceROI).Name("This Distance ROI");
Map(m => m.ThisDistancePlaces).Name("This Distance Places");
Map(m => m.ThisDistancePlaceStrikeRate).Name("This Distance Place Strike Rate");
// Statistiche sulla combinazione pista-distanza
Map(m => m.ThisTrackDistanceRuns).Name("This Track Distance Runs");
Map(m => m.ThisTrackDistanceWins).Name("This Track Distance Wins");
Map(m => m.ThisTrackDistanceStrikeRate).Name("This Track Distance Strike Rate");
Map(m => m.ThisTrackDistanceROI).Name("This Track Distance ROI");
Map(m => m.ThisTrackDistancePlaces).Name("This Track Distance Places");
Map(m => m.ThisTrackDistancePlaceStrikeRate).Name("This Track Distance Place Strike Rate");
// Statistiche sulle condizioni
Map(m => m.ThisConditionRuns).Name("This Condition Runs");
Map(m => m.ThisConditionWins).Name("This Condition Wins");
Map(m => m.ThisConditionStrikeRate).Name("This Condition Strike Rate");
Map(m => m.ThisConditionROI).Name("This Condition ROI");
Map(m => m.ThisConditionPlaces).Name("This Condition Places");
Map(m => m.ThisConditionPlaceStrikeRate).Name("This Condition Place Strike Rate");
// Fantino
Map(m => m.Jockey).Name("Jockey");
Map(m => m.Apprentice).Name("Apprentice");
Map(m => m.JockeyWeightClaim).Name("Jockey Weight Claim");
// Statistiche fantino - ultimi 100
Map(m => m.JockeyLast100HorseEarnings).Name("Jockey Last 100 Horse Earnings");
Map(m => m.JockeyLast100AvgHorseEarnings).Name("Jockey Last 100 Avg Horse Earnings");
Map(m => m.JockeyLast100Starts).Name("Jockey Last 100 Starts");
Map(m => m.JockeyLast100Wins).Name("Jockey Last 100 Wins");
Map(m => m.JockeyLast100StrikeRate).Name("Jockey Last 100 Strike Rate");
Map(m => m.JockeyLast100ROI).Name("Jockey Last 100 ROI");
Map(m => m.JockeyLast100Places).Name("Jockey Last 100 Places");
Map(m => m.JockeyLast100PlaceStrikeRate).Name("Jockey Last 100 Place Strike Rate");
// Statistiche fantino - ultimi 12 mesi
Map(m => m.Jockey12MonthHorseEarnings).Name("Jockey 12 Month Horse Earnings");
Map(m => m.Jockey12MonthAvgHorseEarnings).Name("Jockey 12 Month Avg Horse Earnings");
Map(m => m.Jockey12MonthsStarts).Name("Jockey 12 Months Starts");
Map(m => m.Jockey12MonthsWins).Name("Jockey 12 Months Wins");
Map(m => m.Jockey12MonthsStrikeRate).Name("Jockey 12 Months Strike Rate");
Map(m => m.Jockey12MonthsROI).Name("Jockey 12 Months ROI");
Map(m => m.Jockey12MonthsPlaces).Name("Jockey 12 Months Places");
Map(m => m.Jockey12MonthsPlaceStrikeRate).Name("Jockey 12 Months Place Strike Rate");
// Statistiche fantino - stagione corrente
Map(m => m.JockeyThisSeasonHorseEarnings).Name("Jockey This Season Horse Earnings");
Map(m => m.JockeyThisSeasonAvgHorseEarnings).Name("Jockey This Season Avg Horse Earnings");
Map(m => m.JockeyThisSeasonStarts).Name("Jockey This Season Starts");
Map(m => m.JockeyThisSeasonWins).Name("Jockey This Season Wins");
Map(m => m.JockeyThisSeasonStrikeRate).Name("Jockey This Season Strike Rate");
Map(m => m.JockeyThisSeasonROI).Name("Jockey This Season ROI");
Map(m => m.JockeyThisSeasonPlaces).Name("Jockey This Season Places");
Map(m => m.JockeyThisSeasonPlaceStrikeRate).Name("Jockey This Season Place Strike Rate");
// Statistiche fantino - stagione precedente
Map(m => m.JockeyLastSeasonHorseEarnings).Name("Jockey Last Season Horse Earnings");
Map(m => m.JockeyLastSeasonAvgHorseEarnings).Name("Jockey Last Season Avg Horse Earnings");
Map(m => m.JockeyLastSeasonStarts).Name("Jockey Last Season Starts");
Map(m => m.JockeyLastSeasonWins).Name("Jockey Last Season Wins");
Map(m => m.JockeyLastSeasonStrikeRate).Name("Jockey Last Season Strike Rate");
Map(m => m.JockeyLastSeasonROI).Name("Jockey Last Season ROI");
Map(m => m.JockeyLastSeasonPlaces).Name("Jockey Last Season Places");
Map(m => m.JockeyLastSeasonPlaceStrikeRate).Name("Jockey Last Season Place Strike Rate");
// Allenatore
Map(m => m.Trainer).Name("Trainer");
// Statistiche allenatore - ultimi 100
Map(m => m.TrainerLast100HorseEarnings).Name("Trainer Last 100 Horse Earnings");
Map(m => m.TrainerLast100AvgHorseEarnings).Name("Trainer Last 100 Avg Horse Earnings");
Map(m => m.TrainerLast100Starts).Name("Trainer Last 100 Starts");
Map(m => m.TrainerLast100Wins).Name("Trainer Last 100 Wins");
Map(m => m.TrainerLast100StrikeRate).Name("Trainer Last 100 Strike Rate");
Map(m => m.TrainerLast100ROI).Name("Trainer Last 100 ROI");
Map(m => m.TrainerLast100Places).Name("Trainer Last 100 Places");
Map(m => m.TrainerLast100PlaceStrikeRate).Name("Trainer Last 100 Place Strike Rate");
// Statistiche allenatore - ultimi 12 mesi
Map(m => m.Trainer12MonthHorseEarnings).Name("Trainer 12 Month Horse Earnings");
Map(m => m.Trainer12MonthAvgHorseEarnings).Name("Trainer 12 Month Avg Horse Earnings");
Map(m => m.Trainer12MonthsStarts).Name("Trainer 12 Months Starts");
Map(m => m.Trainer12MonthsWins).Name("Trainer 12 Months Wins");
Map(m => m.Trainer12MonthsStrikeRate).Name("Trainer 12 Months Strike Rate");
Map(m => m.Trainer12MonthsROI).Name("Trainer 12 Months ROI");
Map(m => m.Trainer12MonthsPlaces).Name("Trainer 12 Months Places");
Map(m => m.Trainer12MonthsPlaceStrikeRate).Name("Trainer 12 Months Place Strike Rate");
// Statistiche allenatore - stagione corrente
Map(m => m.TrainerThisSeasonHorseEarnings).Name("Trainer This Season Horse Earnings");
Map(m => m.TrainerThisSeasonAvgHorseEarnings).Name("Trainer This Season Avg Horse Earnings");
Map(m => m.TrainerThisSeasonStarts).Name("Trainer This Season Starts");
Map(m => m.TrainerThisSeasonWins).Name("Trainer This Season Wins");
Map(m => m.TrainerThisSeasonStrikeRate).Name("Trainer This Season Strike Rate");
Map(m => m.TrainerThisSeasonROI).Name("Trainer This Season ROI");
Map(m => m.TrainerThisSeasonPlaces).Name("Trainer This Season Places");
Map(m => m.TrainerThisSeasonPlaceStrikeRate).Name("Trainer This Season Place Strike Rate");
// Statistiche allenatore - stagione precedente
Map(m => m.TrainerLastSeasonHorseEarnings).Name("Trainer Last Season Horse Earnings");
Map(m => m.TrainerLastSeasonAvgHorseEarnings).Name("Trainer Last Season Avg Horse Earnings");
Map(m => m.TrainerLastSeasonStarts).Name("Trainer Last Season Starts");
Map(m => m.TrainerLastSeasonWins).Name("Trainer Last Season Wins");
Map(m => m.TrainerLastSeasonStrikeRate).Name("Trainer Last Season Strike Rate");
Map(m => m.TrainerLastSeasonROI).Name("Trainer Last Season ROI");
Map(m => m.TrainerLastSeasonPlaces).Name("Trainer Last Season Places");
Map(m => m.TrainerLastSeasonPlaceStrikeRate).Name("Trainer Last Season Place Strike Rate");
// Informazioni ultima corsa
Map(m => m.LastStartFinishPosition).Name("Last Start Finish Position");
Map(m => m.LastStartMargin).Name("Last Start Margin");
Map(m => m.LastStartDistance).Name("Last Start Distance");
Map(m => m.LastStartDistanceChange).Name("Last Start Distance Change");
Map(m => m.LastStartPrizeMoney).Name("Last Start Prize Money");
// URL e link
Map(m => m.FormGuideUrl).Name("Form Guide Url");
Map(m => m.HorseProfileUrl).Name("Horse Profile Url");
Map(m => m.JockeyProfileUrl).Name("Jockey Profile Url");
Map(m => m.TrainerProfileUrl).Name("Trainer Profile Url");
// Risultato finale
Map(m => m.FinishResult).Name("Finish Result (Updates after race)", "Finish Result (Updates");
}
}
}
@@ -0,0 +1,536 @@
using CsvHelper.Configuration.Attributes;
using static HorseRacingPredictor.Manager.FileReader;
namespace HorseRacingPredictor.Horses
{
public class HorseRaceData
{
// Informazioni principali sul cavallo
[Name("Num")]
public string Num { get; set; }
[Name("Horse Name")]
public string HorseName { get; set; }
[Name("Age")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Age { get; set; }
[Name("Gender")]
public string Gender { get; set; }
[Name("Handicap Rating")]
[TypeConverter(typeof(NullableIntConverter))]
public int? HandicapRating { get; set; }
// Statistiche di carriera
[Name("Career Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? CareerRuns { get; set; }
[Name("Career Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? CareerWins { get; set; }
[Name("Career Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? CareerStrikeRate { get; set; }
[Name("Career ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? CareerROI { get; set; }
[Name("Career Placings")]
[TypeConverter(typeof(NullableIntConverter))]
public int? CareerPlacings { get; set; }
[Name("Career Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? CareerPlaceStrikeRate { get; set; }
// Statistiche su pista secca
[Name("Dry Track Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? DryTrackRuns { get; set; }
[Name("Dry Track Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? DryTrackWins { get; set; }
[Name("Dry Track Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? DryTrackStrikeRate { get; set; }
[Name("Dry Track ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? DryTrackROI { get; set; }
// Statistiche su pista bagnata
[Name("Wet Track Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? WetTrackRuns { get; set; }
[Name("Wet Track Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? WetTrackWins { get; set; }
[Name("Wet Track Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? WetTrackStrikeRate { get; set; }
[Name("Wet Track ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? WetTrackROI { get; set; }
// Statistiche sui premi
[Name("Average Prize Money")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? AveragePrizeMoney { get; set; }
[Name("Career Prize Money")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? CareerPrizeMoney { get; set; }
// Quote e pesi
[Name("Best Fixed Odds")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? BestFixedOdds { get; set; }
[Name("BetEasy Odds")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? BetEasyOdds { get; set; }
[Name("Weight")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Weight { get; set; }
[Name("Weight Carried")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? WeightCarried { get; set; }
[Name("Barrier")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Barrier { get; set; }
[Name("Prize Money")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? PrizeMoney { get; set; }
// Statistiche sulla pista attuale
[Name("This Track Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackRuns { get; set; }
[Name("This Track Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackWins { get; set; }
[Name("This Track Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackStrikeRate { get; set; }
[Name("This Track ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackROI { get; set; }
[Name("This Track Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackPlaces { get; set; }
[Name("This Track Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackPlaceStrikeRate { get; set; }
// Statistiche sulla distanza
[Name("This Distance Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisDistanceRuns { get; set; }
[Name("This Distance Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisDistanceWins { get; set; }
[Name("This Distance Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisDistanceStrikeRate { get; set; }
[Name("This Distance ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisDistanceROI { get; set; }
[Name("This Distance Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisDistancePlaces { get; set; }
[Name("This Distance Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisDistancePlaceStrikeRate { get; set; }
// Statistiche sulla combinazione pista-distanza
[Name("This Track Distance Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackDistanceRuns { get; set; }
[Name("This Track Distance Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackDistanceWins { get; set; }
[Name("This Track Distance Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackDistanceStrikeRate { get; set; }
[Name("This Track Distance ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackDistanceROI { get; set; }
[Name("This Track Distance Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisTrackDistancePlaces { get; set; }
[Name("This Track Distance Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisTrackDistancePlaceStrikeRate { get; set; }
// Statistiche sulle condizioni
[Name("This Condition Runs")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisConditionRuns { get; set; }
[Name("This Condition Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisConditionWins { get; set; }
[Name("This Condition Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisConditionStrikeRate { get; set; }
[Name("This Condition ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisConditionROI { get; set; }
[Name("This Condition Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? ThisConditionPlaces { get; set; }
[Name("This Condition Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? ThisConditionPlaceStrikeRate { get; set; }
// Informazioni sul fantino
[Name("Jockey")]
public string Jockey { get; set; }
[Name("Apprentice")]
public string Apprentice { get; set; }
[Name("Jockey Weight Claim")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyWeightClaim { get; set; }
// Statistiche fantino - ultimi 100
[Name("Jockey Last 100 Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLast100HorseEarnings { get; set; }
[Name("Jockey Last 100 Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLast100AvgHorseEarnings { get; set; }
[Name("Jockey Last 100 Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLast100Starts { get; set; }
[Name("Jockey Last 100 Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLast100Wins { get; set; }
[Name("Jockey Last 100 Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLast100StrikeRate { get; set; }
[Name("Jockey Last 100 ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLast100ROI { get; set; }
[Name("Jockey Last 100 Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLast100Places { get; set; }
[Name("Jockey Last 100 Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLast100PlaceStrikeRate { get; set; }
// Statistiche fantino - ultimi 12 mesi
[Name("Jockey 12 Month Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Jockey12MonthHorseEarnings { get; set; }
[Name("Jockey 12 Month Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Jockey12MonthAvgHorseEarnings { get; set; }
[Name("Jockey 12 Months Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Jockey12MonthsStarts { get; set; }
[Name("Jockey 12 Months Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Jockey12MonthsWins { get; set; }
[Name("Jockey 12 Months Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Jockey12MonthsStrikeRate { get; set; }
[Name("Jockey 12 Months ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Jockey12MonthsROI { get; set; }
[Name("Jockey 12 Months Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Jockey12MonthsPlaces { get; set; }
[Name("Jockey 12 Months Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Jockey12MonthsPlaceStrikeRate { get; set; }
// Statistiche fantino - stagione corrente
[Name("Jockey This Season Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyThisSeasonHorseEarnings { get; set; }
[Name("Jockey This Season Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyThisSeasonAvgHorseEarnings { get; set; }
[Name("Jockey This Season Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyThisSeasonStarts { get; set; }
[Name("Jockey This Season Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyThisSeasonWins { get; set; }
[Name("Jockey This Season Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyThisSeasonStrikeRate { get; set; }
[Name("Jockey This Season ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyThisSeasonROI { get; set; }
[Name("Jockey This Season Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyThisSeasonPlaces { get; set; }
[Name("Jockey This Season Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyThisSeasonPlaceStrikeRate { get; set; }
// Statistiche fantino - stagione precedente
[Name("Jockey Last Season Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLastSeasonHorseEarnings { get; set; }
[Name("Jockey Last Season Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLastSeasonAvgHorseEarnings { get; set; }
[Name("Jockey Last Season Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLastSeasonStarts { get; set; }
[Name("Jockey Last Season Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLastSeasonWins { get; set; }
[Name("Jockey Last Season Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLastSeasonStrikeRate { get; set; }
[Name("Jockey Last Season ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLastSeasonROI { get; set; }
[Name("Jockey Last Season Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? JockeyLastSeasonPlaces { get; set; }
[Name("Jockey Last Season Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? JockeyLastSeasonPlaceStrikeRate { get; set; }
// Informazioni sull'allenatore
[Name("Trainer")]
public string Trainer { get; set; }
// Statistiche allenatore - ultimi 100
[Name("Trainer Last 100 Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLast100HorseEarnings { get; set; }
[Name("Trainer Last 100 Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLast100AvgHorseEarnings { get; set; }
[Name("Trainer Last 100 Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLast100Starts { get; set; }
[Name("Trainer Last 100 Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLast100Wins { get; set; }
[Name("Trainer Last 100 Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLast100StrikeRate { get; set; }
[Name("Trainer Last 100 ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLast100ROI { get; set; }
[Name("Trainer Last 100 Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLast100Places { get; set; }
[Name("Trainer Last 100 Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLast100PlaceStrikeRate { get; set; }
// Statistiche allenatore - ultimi 12 mesi
[Name("Trainer 12 Month Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Trainer12MonthHorseEarnings { get; set; }
[Name("Trainer 12 Month Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Trainer12MonthAvgHorseEarnings { get; set; }
[Name("Trainer 12 Months Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Trainer12MonthsStarts { get; set; }
[Name("Trainer 12 Months Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Trainer12MonthsWins { get; set; }
[Name("Trainer 12 Months Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Trainer12MonthsStrikeRate { get; set; }
[Name("Trainer 12 Months ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Trainer12MonthsROI { get; set; }
[Name("Trainer 12 Months Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? Trainer12MonthsPlaces { get; set; }
[Name("Trainer 12 Months Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? Trainer12MonthsPlaceStrikeRate { get; set; }
// Statistiche allenatore - stagione corrente
[Name("Trainer This Season Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerThisSeasonHorseEarnings { get; set; }
[Name("Trainer This Season Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerThisSeasonAvgHorseEarnings { get; set; }
[Name("Trainer This Season Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerThisSeasonStarts { get; set; }
[Name("Trainer This Season Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerThisSeasonWins { get; set; }
[Name("Trainer This Season Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerThisSeasonStrikeRate { get; set; }
[Name("Trainer This Season ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerThisSeasonROI { get; set; }
[Name("Trainer This Season Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerThisSeasonPlaces { get; set; }
[Name("Trainer This Season Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerThisSeasonPlaceStrikeRate { get; set; }
// Statistiche allenatore - stagione precedente
[Name("Trainer Last Season Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLastSeasonHorseEarnings { get; set; }
[Name("Trainer Last Season Avg Horse Earnings")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLastSeasonAvgHorseEarnings { get; set; }
[Name("Trainer Last Season Starts")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLastSeasonStarts { get; set; }
[Name("Trainer Last Season Wins")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLastSeasonWins { get; set; }
[Name("Trainer Last Season Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLastSeasonStrikeRate { get; set; }
[Name("Trainer Last Season ROI")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLastSeasonROI { get; set; }
[Name("Trainer Last Season Places")]
[TypeConverter(typeof(NullableIntConverter))]
public int? TrainerLastSeasonPlaces { get; set; }
[Name("Trainer Last Season Place Strike Rate")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? TrainerLastSeasonPlaceStrikeRate { get; set; }
// Informazioni ultima corsa
[Name("Last Start Finish Position")]
[TypeConverter(typeof(NullableIntConverter))]
public int? LastStartFinishPosition { get; set; }
[Name("Last Start Margin")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? LastStartMargin { get; set; }
[Name("Last Start Distance")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? LastStartDistance { get; set; }
[Name("Last Start Distance Change")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? LastStartDistanceChange { get; set; }
[Name("Last Start Prize Money")]
[TypeConverter(typeof(NullableDecimalConverter))]
public decimal? LastStartPrizeMoney { get; set; }
// Link e URL
[Name("Form Guide Url")]
public string FormGuideUrl { get; set; }
[Name("Horse Profile Url")]
public string HorseProfileUrl { get; set; }
[Name("Jockey Profile Url")]
public string JockeyProfileUrl { get; set; }
[Name("Trainer Profile Url")]
public string TrainerProfileUrl { get; set; }
// Risultato finale (aggiornato dopo la corsa)
[Name("Finish Result (Updates after race)")]
public string FinishResult { get; set; }
}
}
@@ -0,0 +1,49 @@
using System;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che contiene il risultato della previsione per un singolo cavallo in una corsa.
/// </summary>
public class HorseRacePrediction
{
// Informazioni identificative
public string HorseId { get; set; }
public string HorseName { get; set; }
public string RaceMeeting { get; set; }
public int RaceNumber { get; set; }
public DateTime RaceDate { get; set; }
// Risultati della previsione
public float PredictedPosition { get; set; }
public float WinProbability { get; set; }
public float PlaceProbability { get; set; }
// Rating ML calcolato (combinazione di probabilità e altri fattori)
public int MLRating { get; set; }
// Quota attuale per valutazione valore
public float CurrentOdds { get; set; }
// Valore calcolato (rapporto tra probabilità e quota)
public float Value => WinProbability > 0 ? (WinProbability * 100) / (1 / CurrentOdds) : 0;
// Indicazione se questo cavallo è considerato di valore
public bool IsValueBet => Value > 1.1f;
// Posizione reale del cavallo
public string ActualPosition { get; set; }
// Indicazione se la previsione è accurata
public bool IsAccuratePrediction { get; set; }
// Metodo per valutare l'accuratezza della previsione
public void EvaluateAccuracy()
{
if (!string.IsNullOrEmpty(ActualPosition) && int.TryParse(ActualPosition, out int actualPos))
{
IsAccuratePrediction = Math.Abs(actualPos - PredictedPosition) <= 0.5;
}
}
}
}
@@ -0,0 +1,573 @@
using Microsoft.ML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Data.SqlClient;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Servizio principale per l'implementazione del machine learning
/// </summary>
public class MachineLearningService
{
private readonly MLContext _mlContext;
private ITransformer _trainedModel;
private readonly string _modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Models", "HorseRaceModel.zip");
public string ConnectionString { get; set; }
public MachineLearningService(string connectionString = null)
{
_mlContext = new MLContext(seed: 42);
Directory.CreateDirectory(Path.GetDirectoryName(_modelPath));
ConnectionString = connectionString;
}
/// <summary>
/// Carica un modello ML esistente da file
/// </summary>
public void LoadModel()
{
if (!File.Exists(_modelPath))
throw new FileNotFoundException($"[ML] Modello non trovato: {_modelPath}");
Console.WriteLine($"[ML] Caricamento modello da: {_modelPath}");
_trainedModel = _mlContext.Model.Load(_modelPath, out _);
Console.WriteLine("[ML] Modello ML caricato correttamente.");
}
/// <summary>
/// Salva il modello addestrato su file
/// </summary>
public void SaveModel(ITransformer model)
{
Console.WriteLine($"[ML] Salvataggio modello in: {_modelPath}");
_mlContext.Model.Save(model, null, _modelPath);
}
/// <summary>
/// Ottiene i dati di training dal database e addestra un modello ML
/// </summary>
public void TrainModel(string connectionString)
{
// Imposta a 'true' per ricreare sempre il modello (default).
// Per NON ricreare il modello ad ogni esecuzione, commenta la riga seguente:
bool alwaysRetrain = true;
// bool alwaysRetrain = false;
if (!alwaysRetrain && File.Exists(_modelPath))
{
Console.WriteLine("[ML] Modello già esistente. Caricamento modello da file...");
LoadModel();
return;
}
try
{
Console.WriteLine("[ML] Inizio caricamento dati di training dal database...");
var trainingData = LoadTrainingDataFromDatabase(connectionString);
if (trainingData.Count == 0)
{
Console.WriteLine("[ML][ERRORE] Nessun dato di training disponibile nel database.");
return;
}
Console.WriteLine($"[ML] Numero record caricati per il training: {trainingData.Count}");
var dataView = _mlContext.Data.LoadFromEnumerable(trainingData);
// Suddividi i dati in train e test (80% train, 20% test)
var split = _mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2, seed: 42);
// Costruisci la pipeline di training
var pipeline = _mlContext.Transforms.Concatenate("Features",
"Age", "HandicapRating", "Weight", "WeightCarried", "Barrier",
"CareerRuns", "CareerWins", "CareerStrikeRate", "CareerROI",
"ThisTrackRuns", "ThisTrackWins", "ThisTrackStrikeRate",
"ThisDistanceRuns", "ThisDistanceWins", "ThisDistanceStrikeRate",
"JockeyLast100Wins", "JockeyLast100StrikeRate",
"TrainerLast100Wins", "TrainerLast100StrikeRate", "BestFixedOdds",
"Rating1", "Rating2", "Rating3", "Rating4", "Rating5"
// Qui puoi aggiungere nuove feature create o derivate
)
.Append(_mlContext.Transforms.NormalizeMinMax("Features"))
.Append(_mlContext.Regression.Trainers.FastTree(
labelColumnName: "FinishPosition",
featureColumnName: "Features",
numberOfTrees: 200, // Aumentato per maggiore accuratezza
numberOfLeaves: 32, // Più foglie per maggiore complessità
minimumExampleCountPerLeaf: 5, // Più sensibile ai dettagli
learningRate: 0.15f // Puoi sperimentare anche questo parametro
));
Console.WriteLine("[ML] Inizio addestramento del modello...");
_trainedModel = pipeline.Fit(split.TrainSet);
Console.WriteLine("[ML] Addestramento completato.");
// Valuta sul training set
var trainMetrics = _mlContext.Regression.Evaluate(_trainedModel.Transform(split.TrainSet), labelColumnName: "FinishPosition");
// Valuta sul test set
var testMetrics = _mlContext.Regression.Evaluate(_trainedModel.Transform(split.TestSet), labelColumnName: "FinishPosition");
// Cross-validation
var cvResults = _mlContext.Regression.CrossValidate(data: dataView, estimator: pipeline, numberOfFolds: 5, labelColumnName: "FinishPosition");
var avgRSquared = cvResults.Average(fold => fold.Metrics.RSquared);
Console.WriteLine($"RSquared medio (cross-validation): {avgRSquared:F4}");
SaveModel(_trainedModel);
Console.WriteLine($"[ML] Modello salvato in: {_modelPath}");
Console.WriteLine("[ML] Metriche di valutazione (TRAIN):");
Console.WriteLine($"[ML] - RSquared: {trainMetrics.RSquared:F4}");
Console.WriteLine($"[ML] - MAE: {trainMetrics.MeanAbsoluteError:F4}");
Console.WriteLine($"[ML] - MSE: {trainMetrics.MeanSquaredError:F4}");
Console.WriteLine($"[ML] - RMSE: {trainMetrics.RootMeanSquaredError:F4}");
Console.WriteLine("[ML] Metriche di valutazione (TEST):");
Console.WriteLine($"[ML] - RSquared: {testMetrics.RSquared:F4}");
Console.WriteLine($"[ML] - MAE: {testMetrics.MeanAbsoluteError:F4}");
Console.WriteLine($"[ML] - MSE: {testMetrics.MeanSquaredError:F4}");
Console.WriteLine($"[ML] - RMSE: {testMetrics.RootMeanSquaredError:F4}");
var predictions = _trainedModel.Transform(split.TestSet);
var actuals = _mlContext.Data.CreateEnumerable<ModelInput>(split.TestSet, false).ToList();
var preds = _mlContext.Data.CreateEnumerable<ModelOutput>(predictions, false).ToList();
int top3Correct = actuals.Zip(preds, (a, p) => (a, p))
.Count(x => x.a.FinishPosition <= 3 && x.p.PredictedPosition <= 3);
double accuracyTop3 = (double)top3Correct / actuals.Count;
Console.WriteLine($"Percentuale cavalli previsti nei primi 3: {accuracyTop3:P2}");
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante l'addestramento del modello: {ex.Message}");
throw;
}
}
/// <summary>
/// Carica i dati di training dal database
/// </summary>
private List<ModelInput> LoadTrainingDataFromDatabase(string connectionString)
{
var trainingData = new List<ModelInput>();
var calculator = new HorseRacingPredictor.Horses.Calculator();
int skippedRows = 0;
int rating1Errors = 0, rating2Errors = 0, rating3Errors = 0, rating4Errors = 0, rating5Errors = 0;
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query = @"
SELECT
CASE WHEN ISNUMERIC(FinishResult) = 1 THEN CAST(FinishResult AS FLOAT) ELSE 999 END AS FinishPosition,
COALESCE(Age, 0) AS Age,
COALESCE(HandicapRating, 0) AS HandicapRating,
COALESCE(Weight, 0) AS Weight,
COALESCE(WeightCarried, 0) AS WeightCarried,
COALESCE(Barrier, 0) AS Barrier,
COALESCE(CareerRuns, 0) AS CareerRuns,
COALESCE(CareerWins, 0) AS CareerWins,
COALESCE(CareerStrikeRate, 0) AS CareerStrikeRate,
COALESCE(CareerROI, 0) AS CareerROI,
COALESCE(ThisTrackRuns, 0) AS ThisTrackRuns,
COALESCE(ThisTrackWins, 0) AS ThisTrackWins,
COALESCE(ThisTrackStrikeRate, 0) AS ThisTrackStrikeRate,
COALESCE(ThisDistanceRuns, 0) AS ThisDistanceRuns,
COALESCE(ThisDistanceWins, 0) AS ThisDistanceWins,
COALESCE(ThisDistanceStrikeRate, 0) AS ThisDistanceStrikeRate,
COALESCE(JockeyLast100Wins, 0) AS JockeyLast100Wins,
COALESCE(JockeyLast100StrikeRate, 0) AS JockeyLast100StrikeRate,
COALESCE(TrainerLast100Wins, 0) AS TrainerLast100Wins,
COALESCE(TrainerLast100StrikeRate, 0) AS TrainerLast100StrikeRate,
COALESCE(BestFixedOdds, 0) AS BestFixedOdds,
COALESCE(Gender, '') AS Gender
FROM Races
WHERE ISNUMERIC(FinishResult) = 1
AND FinishResult IS NOT NULL
AND FinishResult <> ''";
using (var command = new SqlCommand(query, connection))
{
using (var adapter = new SqlDataAdapter(command))
{
var table = new System.Data.DataTable();
adapter.Fill(table);
foreach (System.Data.DataRow row in table.Rows)
{
try {
float finishPos = Convert.ToSingle(row["FinishPosition"]);
if (finishPos >= 15 || finishPos <= 0) {
skippedRows++;
continue;
}
// Calcola i rating con gestione degli errori e sostituzione con valori predefiniti
float rating1 = 0, rating2 = 0, rating3 = 0, rating4 = 0, rating5 = 0;
try {
rating1 = (float)calculator.CalculateRating1(row);
if (float.IsNaN(rating1) || float.IsInfinity(rating1)) {
rating1 = 0;
rating1Errors++;
}
} catch (Exception) {
rating1Errors++;
}
try {
rating2 = (float)calculator.CalculateRating2(row);
if (float.IsNaN(rating2) || float.IsInfinity(rating2)) {
rating2 = 0;
rating2Errors++;
}
} catch (Exception) {
rating2Errors++;
}
try {
rating3 = (float)calculator.CalculateRating3(row);
if (float.IsNaN(rating3) || float.IsInfinity(rating3)) {
rating3 = 0;
rating3Errors++;
}
} catch (Exception) {
rating3Errors++;
}
try {
rating4 = (float)calculator.CalculateRating4(row);
if (float.IsNaN(rating4) || float.IsInfinity(rating4)) {
rating4 = 0;
rating4Errors++;
}
} catch (Exception) {
rating4Errors++;
}
try {
rating5 = (float)calculator.CalculateRating5(row);
if (float.IsNaN(rating5) || float.IsInfinity(rating5)) {
rating5 = 0;
rating5Errors++;
}
} catch (Exception) {
rating5Errors++;
}
// Usa SafeConvert per tutte le conversioni
trainingData.Add(new ModelInput
{
FinishPosition = finishPos,
Age = SafeConvert.ToSingle(row["Age"]),
HandicapRating = SafeConvert.ToSingle(row["HandicapRating"]),
Weight = SafeConvert.ToSingle(row["Weight"]),
WeightCarried = SafeConvert.ToSingle(row["WeightCarried"]),
Barrier = SafeConvert.ToSingle(row["Barrier"]),
CareerRuns = SafeConvert.ToSingle(row["CareerRuns"]),
CareerWins = SafeConvert.ToSingle(row["CareerWins"]),
CareerStrikeRate = SafeConvert.ToSingle(row["CareerStrikeRate"]),
CareerROI = SafeConvert.ToSingle(row["CareerROI"]),
ThisTrackRuns = SafeConvert.ToSingle(row["ThisTrackRuns"]),
ThisTrackWins = SafeConvert.ToSingle(row["ThisTrackWins"]),
ThisTrackStrikeRate = SafeConvert.ToSingle(row["ThisTrackStrikeRate"]),
ThisDistanceRuns = SafeConvert.ToSingle(row["ThisDistanceRuns"]),
ThisDistanceWins = SafeConvert.ToSingle(row["ThisDistanceWins"]),
ThisDistanceStrikeRate = SafeConvert.ToSingle(row["ThisDistanceStrikeRate"]),
JockeyLast100Wins = SafeConvert.ToSingle(row["JockeyLast100Wins"]),
JockeyLast100StrikeRate = SafeConvert.ToSingle(row["JockeyLast100StrikeRate"]),
TrainerLast100Wins = SafeConvert.ToSingle(row["TrainerLast100Wins"]),
TrainerLast100StrikeRate = SafeConvert.ToSingle(row["TrainerLast100StrikeRate"]),
BestFixedOdds = SafeConvert.ToSingle(row["BestFixedOdds"]),
Rating1 = rating1,
Rating2 = rating2,
Rating3 = rating3,
Rating4 = rating4,
Rating5 = rating5
});
} catch (Exception ex) {
skippedRows++;
}
}
Console.WriteLine($"[ML] Record letti dal database: {table.Rows.Count}");
Console.WriteLine($"[ML] Record saltati: {skippedRows}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante il caricamento dei dati dal database: {ex.Message}");
}
Console.WriteLine($"[ML] Caricati {trainingData.Count} record per l'addestramento.");
return trainingData;
}
/// <summary>
/// Costruisce la pipeline di addestramento del modello
/// </summary>
private IEstimator<ITransformer> BuildTrainingPipeline()
{
// Crea la pipeline per addestramento
// 1. Concatena tutte le feature in un unico vettore
// 2. Normalizza i dati di input
// 3. Addestra un modello FastTree con FastTreeRegressionTrainer
var pipeline = _mlContext.Transforms.Concatenate("Features",
"Age", "HandicapRating", "Weight", "WeightCarried", "Barrier",
"CareerRuns", "CareerWins", "CareerStrikeRate", "CareerROI",
"ThisTrackRuns", "ThisTrackWins", "ThisTrackStrikeRate",
"ThisDistanceRuns", "ThisDistanceWins", "ThisDistanceStrikeRate",
"JockeyLast100Wins", "JockeyLast100StrikeRate",
"TrainerLast100Wins", "TrainerLast100StrikeRate", "BestFixedOdds",
"Rating1", "Rating2", "Rating3", "Rating4", "Rating5")
.Append(_mlContext.Transforms.NormalizeMinMax("Features"))
.Append(_mlContext.Regression.Trainers.FastTree(
labelColumnName: "FinishPosition",
featureColumnName: "Features",
numberOfTrees: 100,
numberOfLeaves: 20,
minimumExampleCountPerLeaf: 10));
return pipeline;
}
/// <summary>
/// Valuta il modello addestrato
/// </summary>
private RegressionMetrics EvaluateModel(IDataView dataView, ITransformer model)
{
var predictions = model.Transform(dataView);
return _mlContext.Regression.Evaluate(predictions, labelColumnName: "FinishPosition");
}
/// <summary>
/// Predice la posizione finale per un cavallo
/// </summary>
public ModelOutput PredictFinishPosition(ModelInput input)
{
if (_trainedModel == null)
{
Console.WriteLine("Il modello non è stato addestrato o caricato.");
// Restituisci un oggetto vuoto invece di lanciare un'eccezione
return new ModelOutput
{
PredictedPosition = 0,
WinProbability = 0,
PlaceProbability = 0
};
}
// Crea il motore di previsione
var predEngine = _mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(_trainedModel);
// Effettua la previsione
var prediction = predEngine.Predict(input);
// Calcola le probabilità (implementazione semplificata)
prediction.WinProbability = CalculateWinProbability(prediction.PredictedPosition);
prediction.PlaceProbability = CalculatePlaceProbability(prediction.PredictedPosition);
return prediction;
}
/// <summary>
/// Calcola la probabilità di vittoria in base alla posizione prevista
/// </summary>
private float CalculateWinProbability(float predictedPosition)
{
// Implementazione semplice: più il valore è vicino a 1, maggiore è la probabilità di vittoria
if (predictedPosition <= 1) return 0.9f;
if (predictedPosition <= 2) return 0.5f;
if (predictedPosition <= 3) return 0.3f;
if (predictedPosition <= 5) return 0.1f;
return 0.05f;
}
/// <summary>
/// Calcola la probabilità di piazzamento (primi 3) in base alla posizione prevista
/// </summary>
private float CalculatePlaceProbability(float predictedPosition)
{
// Implementazione semplice: più il valore è vicino a 1, 2 o 3, maggiore è la probabilità di piazzamento
if (predictedPosition <= 1) return 0.95f;
if (predictedPosition <= 2) return 0.85f;
if (predictedPosition <= 3) return 0.75f;
if (predictedPosition <= 4) return 0.4f;
if (predictedPosition <= 6) return 0.2f;
return 0.1f;
}
/// <summary>
/// Analizza un'intera corsa e prevede il risultato per tutti i cavalli
/// </summary>
public List<HorseRacePrediction> PredictRaceResult(string meeting, int raceNumber, DateTime raceDate, string connectionString)
{
var predictions = new List<HorseRacePrediction>();
var calculator = new HorseRacingPredictor.Horses.Calculator(); // Inizializza il calculator qui
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query = @"
SELECT
Num, HorseName,
COALESCE(Age, 0) AS Age,
COALESCE(HandicapRating, 0) AS HandicapRating,
COALESCE(Weight, 0) AS Weight,
COALESCE(WeightCarried, 0) AS WeightCarried,
COALESCE(Barrier, 0) AS Barrier,
COALESCE(CareerRuns, 0) AS CareerRuns,
COALESCE(CareerWins, 0) AS CareerWins,
COALESCE(CareerStrikeRate, 0) AS CareerStrikeRate,
COALESCE(CareerROI, 0) AS CareerROI,
COALESCE(ThisTrackRuns, 0) AS ThisTrackRuns,
COALESCE(ThisTrackWins, 0) AS ThisTrackWins,
COALESCE(ThisTrackStrikeRate, 0) AS ThisTrackStrikeRate,
COALESCE(ThisDistanceRuns, 0) AS ThisDistanceRuns,
COALESCE(ThisDistanceWins, 0) AS ThisDistanceWins,
COALESCE(ThisDistanceStrikeRate, 0) AS ThisDistanceStrikeRate,
COALESCE(JockeyLast100Wins, 0) AS JockeyLast100Wins,
COALESCE(JockeyLast100StrikeRate, 0) AS JockeyLast100StrikeRate,
COALESCE(TrainerLast100Wins, 0) AS TrainerLast100Wins,
COALESCE(TrainerLast100StrikeRate, 0) AS TrainerLast100StrikeRate,
COALESCE(BestFixedOdds, 0) AS BestFixedOdds,
COALESCE(Gender, '') AS Gender
FROM Races
WHERE Meeting = @Meeting AND Race = @Race AND Data = @Data";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@Meeting", meeting);
command.Parameters.AddWithValue("@Race", raceNumber);
command.Parameters.AddWithValue("@Data", raceDate);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// Converti i dati del reader in una DataRow per usare il calculator
DataTable dt = new DataTable();
DataRow row = dt.NewRow();
for (int i = 0; i < reader.FieldCount; i++)
{
if (!dt.Columns.Contains(reader.GetName(i)))
dt.Columns.Add(reader.GetName(i));
row[reader.GetName(i)] = reader.GetValue(i);
}
dt.Rows.Add(row);
// Prepara l'input per il modello
var input = new ModelInput
{
Age = Convert.ToSingle(reader["Age"]),
HandicapRating = Convert.ToSingle(reader["HandicapRating"]),
Weight = Convert.ToSingle(reader["Weight"]),
WeightCarried = Convert.ToSingle(reader["WeightCarried"]),
Barrier = Convert.ToSingle(reader["Barrier"]),
CareerRuns = Convert.ToSingle(reader["CareerRuns"]),
CareerWins = Convert.ToSingle(reader["CareerWins"]),
CareerStrikeRate = Convert.ToSingle(reader["CareerStrikeRate"]),
CareerROI = Convert.ToSingle(reader["CareerROI"]),
ThisTrackRuns = Convert.ToSingle(reader["ThisTrackRuns"]),
ThisTrackWins = Convert.ToSingle(reader["ThisTrackWins"]),
ThisTrackStrikeRate = Convert.ToSingle(reader["ThisTrackStrikeRate"]),
ThisDistanceRuns = Convert.ToSingle(reader["ThisDistanceRuns"]),
ThisDistanceWins = Convert.ToSingle(reader["ThisDistanceWins"]),
ThisDistanceStrikeRate = Convert.ToSingle(reader["ThisDistanceStrikeRate"]),
JockeyLast100Wins = Convert.ToSingle(reader["JockeyLast100Wins"]),
JockeyLast100StrikeRate = Convert.ToSingle(reader["JockeyLast100StrikeRate"]),
TrainerLast100Wins = Convert.ToSingle(reader["TrainerLast100Wins"]),
TrainerLast100StrikeRate = Convert.ToSingle(reader["TrainerLast100StrikeRate"]),
BestFixedOdds = Convert.ToSingle(reader["BestFixedOdds"]),
// Aggiungi i rating calcolati
Rating1 = (float)calculator.CalculateRating1(row),
Rating2 = (float)calculator.CalculateRating2(row),
Rating3 = (float)calculator.CalculateRating3(row),
Rating4 = (float)calculator.CalculateRating4(row),
Rating5 = (float)calculator.CalculateRating5(row)
};
// Effettua la previsione
var output = PredictFinishPosition(input);
// Calcola un rating ML basato sulla posizione prevista
int mlRating = CalculateMLRating(output);
// Aggiungi alla lista delle previsioni
predictions.Add(new HorseRacePrediction
{
HorseId = Convert.ToString(reader["Num"]),
HorseName = Convert.ToString(reader["HorseName"]),
RaceMeeting = meeting,
RaceNumber = raceNumber,
RaceDate = raceDate,
PredictedPosition = output.PredictedPosition,
WinProbability = output.WinProbability,
PlaceProbability = output.PlaceProbability,
MLRating = mlRating,
CurrentOdds = Convert.ToSingle(reader["BestFixedOdds"])
});
}
}
}
}
// Ordina per posizione prevista (dal migliore al peggiore)
return predictions.OrderBy(p => p.PredictedPosition).ToList();
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante la previsione della corsa: {ex.Message}");
return predictions;
}
}
/// <summary>
/// Calcola un rating ML basato sulla posizione prevista e altre metriche
/// </summary>
private int CalculateMLRating(ModelOutput output)
{
// Formula per il rating ML (convertito in un numero intero da 0 a 100)
double baseRating = (1 / Math.Max(0.1, output.PredictedPosition)) * 100;
double winProbabilityFactor = output.WinProbability * 100;
double placeProbabilityFactor = output.PlaceProbability * 50;
// Combina i fattori con pesi diversi
double combinedRating = (baseRating * 0.4) + (winProbabilityFactor * 0.4) + (placeProbabilityFactor * 0.2);
// Limita il valore tra 0 e 100
return (int)Math.Min(100, Math.Max(0, combinedRating));
}
// Metodo di utilità per convertire in modo sicuro i valori
private static class SafeConvert
{
public static float ToSingle(object value, float defaultValue = 0)
{
if (value == null || value == DBNull.Value)
return defaultValue;
try {
float result = Convert.ToSingle(value);
return float.IsNaN(result) || float.IsInfinity(result) ? defaultValue : result;
}
catch {
return defaultValue;
}
}
}
}
}
@@ -0,0 +1,109 @@
using Microsoft.ML.Data;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che rappresenta i dati di input per l'addestramento del modello ML.
/// Contiene le feature selezionate dalle corse passate.
/// </summary>
public class ModelInput
{
// Etichetta da prevedere: posizione finale (1 = vincitore, 2 = secondo posto, ecc.)
[LoadColumn(0)]
[ColumnName("FinishPosition")]
public float FinishPosition { get; set; }
// Feature principali sul cavallo
[LoadColumn(1)]
[ColumnName("Age")]
public float Age { get; set; }
[LoadColumn(2)]
[ColumnName("HandicapRating")]
public float HandicapRating { get; set; }
[LoadColumn(3)]
[ColumnName("Weight")]
public float Weight { get; set; }
[LoadColumn(4)]
[ColumnName("WeightCarried")]
public float WeightCarried { get; set; }
[LoadColumn(5)]
[ColumnName("Barrier")]
public float Barrier { get; set; }
// Statistiche di carriera
[LoadColumn(6)]
[ColumnName("CareerRuns")]
public float CareerRuns { get; set; }
[LoadColumn(7)]
[ColumnName("CareerWins")]
public float CareerWins { get; set; }
[LoadColumn(8)]
[ColumnName("CareerStrikeRate")]
public float CareerStrikeRate { get; set; }
[LoadColumn(9)]
[ColumnName("CareerROI")]
public float CareerROI { get; set; }
// Statistiche di pista
[LoadColumn(10)]
[ColumnName("ThisTrackRuns")]
public float ThisTrackRuns { get; set; }
[LoadColumn(11)]
[ColumnName("ThisTrackWins")]
public float ThisTrackWins { get; set; }
[LoadColumn(12)]
[ColumnName("ThisTrackStrikeRate")]
public float ThisTrackStrikeRate { get; set; }
[LoadColumn(13)]
[ColumnName("ThisDistanceRuns")]
public float ThisDistanceRuns { get; set; }
[LoadColumn(14)]
[ColumnName("ThisDistanceWins")]
public float ThisDistanceWins { get; set; }
[LoadColumn(15)]
[ColumnName("ThisDistanceStrikeRate")]
public float ThisDistanceStrikeRate { get; set; }
// Statistiche fantino
[LoadColumn(16)]
[ColumnName("JockeyLast100Wins")]
public float JockeyLast100Wins { get; set; }
[LoadColumn(17)]
[ColumnName("JockeyLast100StrikeRate")]
public float JockeyLast100StrikeRate { get; set; }
// Statistiche allenatore
[LoadColumn(18)]
[ColumnName("TrainerLast100Wins")]
public float TrainerLast100Wins { get; set; }
[LoadColumn(19)]
[ColumnName("TrainerLast100StrikeRate")]
public float TrainerLast100StrikeRate { get; set; }
// Quota iniziale (può essere predittiva)
[LoadColumn(20)]
[ColumnName("BestFixedOdds")]
public float BestFixedOdds { get; set; }
// Cambia il tipo da int a float
public float Rating1 { get; set; }
public float Rating2 { get; set; }
public float Rating3 { get; set; }
public float Rating4 { get; set; }
public float Rating5 { get; set; }
}
}
@@ -0,0 +1,20 @@
using Microsoft.ML.Data;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che rappresenta l'output della previsione del modello ML.
/// </summary>
public class ModelOutput
{
// Previsione: il risultato della classificazione (1 = vincitore, 2 = secondo posto, ecc.)
[ColumnName("Score")]
public float PredictedPosition { get; set; }
[NoColumn] // Indica che questa proprietà non esiste nei dati
public float WinProbability { get; set; }
[NoColumn] // Indica che questa proprietà non esiste nei dati
public float PlaceProbability { get; set; }
}
}
+360
View File
@@ -0,0 +1,360 @@
using System;
using System.Data;
using System.Windows.Forms;
namespace BettingPredictor
{
partial class Main
{
// Container
private System.ComponentModel.IContainer components = null;
// Grafica
private TabControl tabControl;
// Horse tab
private TabPage tabPageHorse;
private TextBox textBoxFolderPath;
private Button buttonBrowse;
private Button buttonPredict; // New button for predictions
private Button buttonImport; // New button for import
private ProgressBar progressBarHorse;
private Label labelStatusHorse;
private DataGridView dataGridViewHorse;
// Replace the buttonLoadFootball with buttonImportFootball and add buttonDownloadFootball
// Football tab
private TabPage tabPageFootball;
private DateTimePicker dateTimePicker;
private Button buttonImportFootball;
private Button buttonDownloadFootball;
private DataGridView dataGridViewFootball;
private ProgressBar progressBarFootball;
private Label labelStatusFootball;
// ApiOptions class definition - rimosso GetLeagueData
public static class ApiOptions
{
public const string GetLeagueFixtures = "Visualizza elenco partite alla data";
public const string GetLeagueOdds = "Visualizza elenco quote partite";
public static readonly string[] AllOptions = { GetLeagueFixtures, GetLeagueOdds };
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tabControl = new System.Windows.Forms.TabControl();
// Horse Tab
this.tabPageHorse = new System.Windows.Forms.TabPage();
this.textBoxFolderPath = new System.Windows.Forms.TextBox();
this.buttonBrowse = new System.Windows.Forms.Button();
this.buttonPredict = new System.Windows.Forms.Button(); // New button for predictions
this.buttonImport = new System.Windows.Forms.Button(); // New button for import
this.progressBarHorse = new System.Windows.Forms.ProgressBar();
this.labelStatusHorse = new System.Windows.Forms.Label();
this.dataGridViewHorse = new System.Windows.Forms.DataGridView();
// In the InitializeComponent method, update:
// Football Tab
this.tabPageFootball = new System.Windows.Forms.TabPage();
this.dateTimePicker = new System.Windows.Forms.DateTimePicker();
this.buttonImportFootball = new System.Windows.Forms.Button();
this.buttonDownloadFootball = new System.Windows.Forms.Button();
this.progressBarFootball = new System.Windows.Forms.ProgressBar();
this.labelStatusFootball = new System.Windows.Forms.Label();
this.dataGridViewFootball = new System.Windows.Forms.DataGridView();
this.tabControl.SuspendLayout();
this.tabPageHorse.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewHorse)).BeginInit();
this.tabPageFootball.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewFootball)).BeginInit();
this.SuspendLayout();
//
// tabControl
//
this.tabControl.Controls.Add(this.tabPageHorse);
this.tabControl.Controls.Add(this.tabPageFootball);
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl.Location = new System.Drawing.Point(0, 0);
this.tabControl.Name = "tabControl";
this.tabControl.SelectedIndex = 0;
this.tabControl.Size = new System.Drawing.Size(800, 450);
this.tabControl.TabIndex = 0;
//
// tabPageHorse
//
this.tabPageHorse.Controls.Add(this.dataGridViewHorse);
this.tabPageHorse.Controls.Add(this.labelStatusHorse);
this.tabPageHorse.Controls.Add(this.progressBarHorse);
this.tabPageHorse.Controls.Add(this.buttonPredict); // Add new button
this.tabPageHorse.Controls.Add(this.buttonImport); // Add new button
this.tabPageHorse.Controls.Add(this.buttonBrowse);
this.tabPageHorse.Controls.Add(this.textBoxFolderPath);
this.tabPageHorse.Location = new System.Drawing.Point(4, 22);
this.tabPageHorse.Name = "tabPageHorse";
this.tabPageHorse.Padding = new System.Windows.Forms.Padding(3);
this.tabPageHorse.Size = new System.Drawing.Size(792, 424);
this.tabPageHorse.TabIndex = 0;
this.tabPageHorse.Text = "Horse";
this.tabPageHorse.UseVisualStyleBackColor = true;
//
// textBoxFolderPath
//
this.textBoxFolderPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxFolderPath.Location = new System.Drawing.Point(8, 8);
this.textBoxFolderPath.Name = "textBoxFolderPath";
this.textBoxFolderPath.ReadOnly = true;
this.textBoxFolderPath.Size = new System.Drawing.Size(510, 20);
this.textBoxFolderPath.TabIndex = 0;
//
// buttonBrowse
//
this.buttonBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonBrowse.Location = new System.Drawing.Point(524, 6); // Moved position to make room for Predict button
this.buttonBrowse.Size = new System.Drawing.Size(80, 23);
this.buttonBrowse.Name = "buttonBrowse";
this.buttonBrowse.TabIndex = 1;
this.buttonBrowse.Text = "Sfoglia...";
this.buttonBrowse.UseVisualStyleBackColor = true;
this.buttonBrowse.Click += new System.EventHandler(this.buttonBrowse_Click);
//
// buttonImport
//
this.buttonImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonImport.Location = new System.Drawing.Point(610, 6);
this.buttonImport.Name = "buttonImport";
this.buttonImport.Size = new System.Drawing.Size(80, 23);
this.buttonImport.TabIndex = 6;
this.buttonImport.Text = "Importa";
this.buttonImport.UseVisualStyleBackColor = true;
this.buttonImport.Click += new System.EventHandler(this.buttonImport_Click);
//
// buttonPredict
//
this.buttonPredict.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonPredict.Location = new System.Drawing.Point(696, 6);
this.buttonPredict.Name = "buttonPredict";
this.buttonPredict.Size = new System.Drawing.Size(80, 23);
this.buttonPredict.TabIndex = 5;
this.buttonPredict.Text = "Predici";
this.buttonPredict.UseVisualStyleBackColor = true;
this.buttonPredict.Click += new System.EventHandler(this.buttonPredict_Click);
//
// progressBarHorse
//
this.progressBarHorse.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBarHorse.Location = new System.Drawing.Point(8, 34);
this.progressBarHorse.Name = "progressBarHorse";
this.progressBarHorse.Size = new System.Drawing.Size(762, 20);
this.progressBarHorse.TabIndex = 2;
//
// labelStatusHorse
//
this.labelStatusHorse.AutoSize = true;
this.labelStatusHorse.Location = new System.Drawing.Point(8, 57);
this.labelStatusHorse.Name = "labelStatusHorse";
this.labelStatusHorse.Size = new System.Drawing.Size(38, 13);
this.labelStatusHorse.TabIndex = 3;
this.labelStatusHorse.Text = "Pronto";
//
// dataGridViewHorse
//
this.dataGridViewHorse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridViewHorse.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewHorse.Location = new System.Drawing.Point(8, 73);
this.dataGridViewHorse.Name = "dataGridViewHorse";
this.dataGridViewHorse.ReadOnly = true;
this.dataGridViewHorse.Size = new System.Drawing.Size(762, 343);
this.dataGridViewHorse.TabIndex = 4;
// Update controls add to the tab page:
this.tabPageFootball.Controls.Add(this.dataGridViewFootball);
this.tabPageFootball.Controls.Add(this.labelStatusFootball);
this.tabPageFootball.Controls.Add(this.progressBarFootball);
this.tabPageFootball.Controls.Add(this.buttonImportFootball);
this.tabPageFootball.Controls.Add(this.buttonDownloadFootball);
this.tabPageFootball.Controls.Add(this.dateTimePicker);
this.tabPageFootball.Location = new System.Drawing.Point(4, 22);
this.tabPageFootball.Name = "tabPageFootball";
this.tabPageFootball.Padding = new System.Windows.Forms.Padding(3);
this.tabPageFootball.Size = new System.Drawing.Size(792, 424);
this.tabPageFootball.TabIndex = 1;
this.tabPageFootball.Text = "Football";
this.tabPageFootball.UseVisualStyleBackColor = true;
//
// dateTimePicker
//
this.dateTimePicker.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dateTimePicker.Location = new System.Drawing.Point(8, 8);
this.dateTimePicker.Name = "dateTimePicker";
// Update dateTimePicker size to make room for two buttons
this.dateTimePicker.Size = new System.Drawing.Size(596, 20);
this.dateTimePicker.TabIndex = 1;
this.dateTimePicker.Value = System.DateTime.Today;
// buttonDownloadFootball properties
this.buttonDownloadFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonDownloadFootball.Location = new System.Drawing.Point(610, 6);
this.buttonDownloadFootball.Name = "buttonDownloadFootball";
this.buttonDownloadFootball.Size = new System.Drawing.Size(75, 23);
this.buttonDownloadFootball.TabIndex = 6;
this.buttonDownloadFootball.Text = "Scarica";
this.buttonDownloadFootball.UseVisualStyleBackColor = true;
this.buttonDownloadFootball.Click += new System.EventHandler(this.buttonDownloadFootball_Click);
// buttonImportFootball properties (replace buttonLoadFootball)
this.buttonImportFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonImportFootball.Location = new System.Drawing.Point(695, 6);
this.buttonImportFootball.Name = "buttonImportFootball";
this.buttonImportFootball.Size = new System.Drawing.Size(75, 23);
this.buttonImportFootball.TabIndex = 2;
this.buttonImportFootball.Text = "Importa";
this.buttonImportFootball.UseVisualStyleBackColor = true;
this.buttonImportFootball.Click += new System.EventHandler(this.buttonImportFootball_Click);
//
// progressBarFootball
//
this.progressBarFootball.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBarFootball.Location = new System.Drawing.Point(8, 35);
this.progressBarFootball.Name = "progressBarFootball";
this.progressBarFootball.Size = new System.Drawing.Size(762, 20);
this.progressBarFootball.TabIndex = 3;
//
// labelStatusFootball
//
this.labelStatusFootball.AutoSize = true;
this.labelStatusFootball.Location = new System.Drawing.Point(8, 58);
this.labelStatusFootball.Name = "labelStatusFootball";
this.labelStatusFootball.Size = new System.Drawing.Size(38, 13);
this.labelStatusFootball.TabIndex = 4;
this.labelStatusFootball.Text = "Pronto";
//
// dataGridViewFootball
//
this.dataGridViewFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridViewFootball.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewFootball.Location = new System.Drawing.Point(8, 74);
this.dataGridViewFootball.Name = "dataGridViewFootball";
this.dataGridViewFootball.ReadOnly = true;
this.dataGridViewFootball.Size = new System.Drawing.Size(762, 342);
this.dataGridViewFootball.TabIndex = 5;
//
// Main
//
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.tabControl);
this.Name = "Main";
this.Text = "Betting Predictor";
this.Load += new System.EventHandler(this.Main_Load);
this.tabControl.ResumeLayout(false);
this.tabPageHorse.ResumeLayout(false);
this.tabPageHorse.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewHorse)).EndInit();
this.tabPageFootball.ResumeLayout(false);
this.tabPageFootball.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewFootball)).EndInit();
this.ResumeLayout(false);
}
}
public partial class ProgressDialog : Form
{
public ProgressDialog()
{
InitializeComponent();
}
public void UpdateProgress(int currentPage, int totalPages)
{
progressBar.Maximum = totalPages;
progressBar.Value = currentPage;
labelProgress.Text = $"Scaricamento pagina {currentPage} di {totalPages}";
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.progressBar = new System.Windows.Forms.ProgressBar();
this.labelProgress = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// progressBar
//
this.progressBar.Location = new System.Drawing.Point(12, 12);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(360, 23);
this.progressBar.TabIndex = 0;
//
// labelProgress
//
this.labelProgress.AutoSize = true;
this.labelProgress.Location = new System.Drawing.Point(12, 38);
this.labelProgress.Name = "labelProgress";
this.labelProgress.Size = new System.Drawing.Size(0, 13);
this.labelProgress.TabIndex = 1;
//
// ProgressDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(384, 61);
this.Controls.Add(this.labelProgress);
this.Controls.Add(this.progressBar);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ProgressDialog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Scaricamento in corso...";
this.ResumeLayout(false);
this.PerformLayout();
}
private System.ComponentModel.IContainer components = null;
private ProgressBar progressBar;
private Label labelProgress;
}
}
@@ -0,0 +1,747 @@
using HorseRacingPredictor.Football;
using HorseRacingPredictor.Football.Manager;
using HorseRacingPredictor.Horses;
using HorseRacingPredictor.Horses.ML;
using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BettingPredictor
{
public partial class Main : Form
{
private readonly HorseRacingPredictor.Horses.Calculator calculator;
private readonly HorseRacingPredictor.Horses.Database horseDbManager;
private readonly HorseRacingPredictor.Horses.FileReader fileReaderHorses;
private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
// Nuova classe centralizzata per l'API Football
private readonly HorseRacingPredictor.Football.Main footballManager;
private readonly MachineLearningService mlService = new MachineLearningService();
// Memorizza i dati importati per utilizzarli nella fase di predizione
private DataTable importedHorsesData;
public Main()
{
InitializeComponent();
calculator = new HorseRacingPredictor.Horses.Calculator();
horseDbManager = new HorseRacingPredictor.Horses.Database();
fileReaderHorses = new HorseRacingPredictor.Horses.FileReader();
// Inizializza il gestore centralizzato del Football
footballManager = new HorseRacingPredictor.Football.Main();
// Disabilita il pulsante di importazione fino a quando non viene selezionato un percorso
buttonImport.Enabled = false;
// Il pulsante di predizione deve essere sempre abilitato
buttonPredict.Enabled = true;
}
/// <summary>
/// Importa i dati dei file CSV delle corse dei cavalli
/// </summary>
private void ImportCsvFilesHorse(string folderPath)
{
if (string.IsNullOrEmpty(folderPath))
{
MessageBox.Show("Seleziona un percorso valido.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
// Usa il metodo FileReaderManagerHorses per ottenere i file CSV
var csvFiles = fileReaderHorses.GetHorseRaceFiles(folderPath);
if (csvFiles.Count == 0)
{
MessageBox.Show("Nessun file CSV trovato nel percorso selezionato.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Creiamo una tabella combinata per tutti i dati
DataTable combinedTable = null;
// Inizializza la progress bar
progressBarHorse.Minimum = 0;
progressBarHorse.Maximum = csvFiles.Count;
progressBarHorse.Value = 0;
labelStatusHorse.Text = "Inizializzazione importazione...";
// Utilizziamo Task per non bloccare l'UI
Task.Run(() =>
{
for (int i = 0; i < csvFiles.Count; i++)
{
var file = csvFiles[i];
// Aggiorna la UI con lo stato corrente
this.Invoke(new Action(() =>
{
progressBarHorse.Value = i + 1;
labelStatusHorse.Text = $"Importazione file {i + 1} di {csvFiles.Count}: {Path.GetFileName(file)}";
}));
try
{
// Leggi i dati dal file CSV
var horsesData = fileReaderHorses.ReadHorseDataFromFile(file);
// Al primo file, creiamo la tabella combinata
if (combinedTable == null)
{
// Creiamo una nuova tabella vuota
combinedTable = new DataTable();
// Aggiungiamo le colonne necessarie
combinedTable.Columns.Add("File", typeof(string));
combinedTable.Columns.Add("Data Corsa", typeof(DateTime));
combinedTable.Columns.Add("Meeting", typeof(string));
combinedTable.Columns.Add("Corsa", typeof(int));
combinedTable.Columns.Add("Numero", typeof(string));
combinedTable.Columns.Add("Nome Cavallo", typeof(string));
combinedTable.Columns.Add("Età", typeof(int));
combinedTable.Columns.Add("Genere", typeof(string));
combinedTable.Columns.Add("Peso", typeof(decimal));
combinedTable.Columns.Add("Quota", typeof(decimal));
combinedTable.Columns.Add("Risultato Reale", typeof(string));
// La colonna "Posizione Prevista" verrà aggiunta nella fase di predizione
}
// Inserisci i dati nel database
horseDbManager.ProcessAndInsertHorseRaceData(file);
// Estrai le informazioni sulla corsa dal nome del file
var raceInfo = fileReaderHorses.GetRaceInfoFromFileName(file);
// Aggiungi i dati dei cavalli alla tabella combinata (senza predizioni)
foreach (var horseData in horsesData)
{
DataRow newRow = combinedTable.NewRow();
newRow["File"] = Path.GetFileName(file);
newRow["Data Corsa"] = raceInfo.RaceDate;
newRow["Meeting"] = raceInfo.MeetingName;
newRow["Corsa"] = raceInfo.RaceNumber;
newRow["Numero"] = horseData.Num;
newRow["Nome Cavallo"] = horseData.HorseName;
newRow["Età"] = horseData.Age;
newRow["Genere"] = horseData.Gender;
newRow["Peso"] = horseData.WeightCarried;
newRow["Quota"] = horseData.BestFixedOdds;
newRow["Risultato Reale"] = horseData.FinishResult;
combinedTable.Rows.Add(newRow);
}
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
MessageBox.Show($"Errore durante l'elaborazione del file {Path.GetFileName(file)}: {ex.Message}",
"Errore Elaborazione", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}));
// Continua con il prossimo file
}
}
// Completamento dell'operazione
this.Invoke(new Action(() =>
{
if (combinedTable != null && combinedTable.Rows.Count > 0)
{
// Salva i dati importati nella variabile di classe
importedHorsesData = combinedTable;
// Mostra i dati nella DataGridView
dataGridViewHorse.DataSource = importedHorsesData;
// Formatta la griglia
FormatHorseDataGrid(dataGridViewHorse);
labelStatusHorse.Text = $"Importazione completata: {combinedTable.Rows.Count} cavalli importati";
}
else
{
labelStatusHorse.Text = "Nessun dato valido trovato nei file CSV";
}
// Riabilita il pulsante importa al termine dell'operazione
buttonImport.Enabled = true;
}));
});
}
catch (Exception ex)
{
MessageBox.Show($"Errore durante il caricamento dei file CSV: {ex.Message}",
"Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
labelStatusHorse.Text = "Errore durante l'importazione";
}
}
private void ShowStrikeRateStats(DataTable table)
{
// Raggruppa per gara usando Data Corsa, Meeting, Corsa
var grouped = table.AsEnumerable()
.Where(row => row["Risultato Reale"] != DBNull.Value && row["Posizione Prevista"] != DBNull.Value)
.GroupBy(row => new {
DataCorsa = row["Data Corsa"],
Meeting = row["Meeting"].ToString(),
Corsa = row["Corsa"]
});
string stats = "Statistiche Strike Rate per Gara:\n";
int gareTotali = 0, winTotali = 0, placeTotali = 0;
foreach (var group in grouped)
{
gareTotali++;
int tot = 0, winOk = 0, placeOk = 0;
foreach (var row in group)
{
if (int.TryParse(row["Risultato Reale"].ToString(), out int real) &&
float.TryParse(row["Posizione Prevista"].ToString(), out float pred))
{
tot++;
if (real == 1 && pred <= 1.5f) winOk++;
if (real <= 3 && pred <= 3.5f) placeOk++;
}
}
winTotali += winOk;
placeTotali += placeOk;
stats += $"Gara: {group.Key.DataCorsa:dd/MM/yyyy} - {group.Key.Meeting} - N°{group.Key.Corsa}\n" +
$" Cavalli: {tot}\n" +
$" Vincitore previsto corretto: {winOk} ({(winOk * 100.0 / tot):F1}%)\n" +
$" Piazzati previsti corretti: {placeOk} ({(placeOk * 100.0 / tot):F1}%)\n\n";
}
if (gareTotali > 0)
{
stats += $"Totale gare: {gareTotali}\n" +
$"Totale vincitori previsti corretti: {winTotali}\n" +
$"Totale piazzati previsti corretti: {placeTotali}\n";
MessageBox.Show(stats, "Strike Rate per Gara", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
/// <summary>
/// Formatta la griglia dei dati dei cavalli per una migliore visualizzazione
/// </summary>
private void FormatHorseDataGrid(DataGridView grid)
{
if (grid.Columns.Count == 0)
return;
// Nascondi le colonne Età, Genere e Peso
foreach (var colName in new[] { "Età", "Genere", "Peso" })
{
if (grid.Columns.Contains(colName))
grid.Columns[colName].Visible = false;
}
// Imposta l'ordine delle colonne principali
if (grid.Columns.Contains("File"))
grid.Columns["File"].DisplayIndex = 0;
if (grid.Columns.Contains("Data Corsa"))
grid.Columns["Data Corsa"].DisplayIndex = 1;
if (grid.Columns.Contains("Meeting"))
grid.Columns["Meeting"].DisplayIndex = 2;
if (grid.Columns.Contains("Corsa"))
grid.Columns["Corsa"].DisplayIndex = 3;
if (grid.Columns.Contains("Numero"))
grid.Columns["Numero"].DisplayIndex = 4;
if (grid.Columns.Contains("Nome Cavallo"))
grid.Columns["Nome Cavallo"].DisplayIndex = 5;
if (grid.Columns.Contains("Quota"))
grid.Columns["Quota"].DisplayIndex = 6;
if (grid.Columns.Contains("Risultato Reale"))
grid.Columns["Risultato Reale"].DisplayIndex = 7;
if (grid.Columns.Contains("Posizione Prevista"))
grid.Columns["Posizione Prevista"].DisplayIndex = 8;
// Imposta il formato delle date
if (grid.Columns.Contains("Data Corsa"))
grid.Columns["Data Corsa"].DefaultCellStyle.Format = "dd/MM/yyyy";
// Imposta il formato numerico per le quote
if (grid.Columns.Contains("Quota"))
grid.Columns["Quota"].DefaultCellStyle.Format = "N2";
if (grid.Columns.Contains("Posizione Prevista"))
grid.Columns["Posizione Prevista"].DefaultCellStyle.Format = "N1";
// Imposta l'allineamento al centro per alcune colonne
foreach (var columnName in new[] { "Numero", "Età", "Corsa", "Risultato Reale", "Posizione Prevista" })
{
if (grid.Columns.Contains(columnName))
grid.Columns[columnName].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
// Colora le righe in base al risultato
grid.CellFormatting += (sender, e) =>
{
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
{
var row = grid.Rows[e.RowIndex];
if (grid.Columns.Contains("Risultato Reale") && row.Cells["Risultato Reale"].Value != null)
{
string resultStr = row.Cells["Risultato Reale"].Value.ToString();
if (int.TryParse(resultStr, out int result))
{
if (result == 1)
row.DefaultCellStyle.BackColor = System.Drawing.Color.LightGreen;
else if (result <= 3)
row.DefaultCellStyle.BackColor = System.Drawing.Color.LightYellow;
}
}
if (grid.Columns.Contains("Risultato Reale") && grid.Columns.Contains("Posizione Prevista"))
{
var realResultCell = row.Cells["Risultato Reale"];
var predictedCell = row.Cells["Posizione Prevista"];
if (realResultCell.Value != null && predictedCell.Value != null)
{
if (int.TryParse(realResultCell.Value.ToString(), out int realPos) &&
float.TryParse(predictedCell.Value.ToString(), out float predictedPos))
{
if (Math.Abs(realPos - predictedPos) <= 0.5)
{
predictedCell.Style.BackColor = System.Drawing.Color.LightGreen;
predictedCell.Style.ForeColor = System.Drawing.Color.DarkGreen;
predictedCell.Style.Font = new System.Drawing.Font(grid.Font, System.Drawing.FontStyle.Bold);
}
}
}
}
}
};
// Abilita il riordino delle colonne
grid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
grid.AllowUserToOrderColumns = true;
}
// Add these methods to handle the download and import operations
/// <summary>
/// Nuovo metodo per gestire lo scaricamento dei dati calcistici
/// </summary>
private async Task DownloadFootballDataAsync(DateTime selectedDate)
{
try
{
// Inizializza e mostra la barra di avanzamento
progressBarFootball.Minimum = 0;
progressBarFootball.Maximum = 100;
progressBarFootball.Value = 0;
labelStatusFootball.Text = "Scaricamento delle partite in corso...";
// Disabilita i controlli durante l'elaborazione
dateTimePicker.Enabled = false;
buttonDownloadFootball.Enabled = false;
buttonImportFootball.Enabled = false;
// Crea gli oggetti progress che aggiornano la UI in modo thread-safe
var progressCallback = new Progress<int>(value =>
{
progressBarFootball.Value = value;
});
var statusCallback = new Progress<string>(status =>
{
labelStatusFootball.Text = status;
});
// Usa la nuova classe centralizzata del Football per scaricare i dati
await Task.Run(() =>
footballManager.DownloadFixturesAndOdds(selectedDate, progressCallback, statusCallback));
// Riabilita i controlli
dateTimePicker.Enabled = true;
buttonDownloadFootball.Enabled = true;
buttonImportFootball.Enabled = true;
labelStatusFootball.Text = "Scaricamento completato. Pronto per l'importazione.";
}
catch (Exception ex)
{
// Gestione degli errori
MessageBox.Show($"Errore durante lo scaricamento dei dati calcistici: {ex.Message}",
"Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Riabilita i controlli anche in caso di errore
dateTimePicker.Enabled = true;
buttonDownloadFootball.Enabled = true;
buttonImportFootball.Enabled = true;
labelStatusFootball.Text = "Errore nello scaricamento dati";
progressBarFootball.Value = 0;
}
}
/// <summary>
/// Nuovo metodo per gestire l'importazione dei dati calcistici
/// </summary>
private async Task ImportFootballDataAsync(DataGridView dataGridViewFootball)
{
try
{
// Inizializza e mostra la barra di avanzamento
progressBarFootball.Minimum = 0;
progressBarFootball.Maximum = 100;
progressBarFootball.Value = 0;
labelStatusFootball.Text = "Importazione dati dalle risposte API in corso...";
// Disabilita i controlli durante l'elaborazione
dateTimePicker.Enabled = false;
buttonDownloadFootball.Enabled = false;
buttonImportFootball.Enabled = false;
// Crea gli oggetti progress che aggiornano la UI in modo thread-safe
var progressCallback = new Progress<int>(value =>
{
progressBarFootball.Value = value;
});
var statusCallback = new Progress<string>(status =>
{
labelStatusFootball.Text = status;
});
// Usa la nuova classe centralizzata del Football per importare i dati
var footballDataTable = await Task.Run(() =>
footballManager.ImportFromApiResponses(progressCallback, statusCallback));
// Aggiorna la UI con i risultati (già thread-safe perché eseguito sul thread UI)
dataGridViewFootball.DataSource = footballDataTable;
// Formatta la griglia dati
FormatFootballDataGrid(dataGridViewFootball);
// Riabilita i controlli
dateTimePicker.Enabled = true;
buttonDownloadFootball.Enabled = true;
buttonImportFootball.Enabled = true;
labelStatusFootball.Text = $"Importati {footballDataTable?.Rows.Count ?? 0} eventi";
}
catch (Exception ex)
{
// Gestione degli errori
MessageBox.Show($"Errore durante l'importazione dei dati calcistici: {ex.Message}",
"Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Riabilita i controlli anche in caso di errore
dateTimePicker.Enabled = true;
buttonDownloadFootball.Enabled = true;
buttonImportFootball.Enabled = true;
labelStatusFootball.Text = "Errore nell'importazione dati";
progressBarFootball.Value = 0;
}
}
/// <summary>
/// Metodo aggiornato per il processo completo (scaricamento + importazione)
/// </summary>
private async Task ProcessFootballDataAsync(DateTime selectedDate, DataGridView dataGridViewFootball)
{
try
{
// Prima scarica, poi importa
await DownloadFootballDataAsync(selectedDate);
await ImportFootballDataAsync(dataGridViewFootball);
}
catch (Exception ex)
{
// Gestione degli errori
MessageBox.Show($"Errore durante l'elaborazione dei dati calcistici: {ex.Message}",
"Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Reset dello stato UI
dateTimePicker.Enabled = true;
buttonDownloadFootball.Enabled = true;
buttonImportFootball.Enabled = true;
labelStatusFootball.Text = "Errore nell'elaborazione dati";
progressBarFootball.Value = 0;
}
}
/// <summary>
/// Formatta la griglia dei dati calcistici per una migliore visualizzazione
/// </summary>
private void FormatFootballDataGrid(DataGridView grid)
{
if (grid.Columns.Count == 0)
return;
// Imposta l'ordine delle colonne principali
if (grid.Columns.Contains("Data / Ora"))
grid.Columns["Data / Ora"].DisplayIndex = 0;
if (grid.Columns.Contains("Paese"))
grid.Columns["Paese"].DisplayIndex = 1;
if (grid.Columns.Contains("Campionato"))
grid.Columns["Campionato"].DisplayIndex = 2;
if (grid.Columns.Contains("Casa"))
grid.Columns["Casa"].DisplayIndex = 3;
if (grid.Columns.Contains("Trasferta"))
grid.Columns["Trasferta"].DisplayIndex = 4;
if (grid.Columns.Contains("Previsione"))
grid.Columns["Previsione"].DisplayIndex = 5;
// Imposta il formato delle date
if (grid.Columns.Contains("Data / Ora"))
grid.Columns["Data / Ora"].DefaultCellStyle.Format = "dd/MM/yyyy HH:mm";
// Imposta il formato numerico per le quote
foreach (var quotaColumn in new[] { "Quota Casa", "Quota Pareggio", "Quota Trasferta" })
{
if (grid.Columns.Contains(quotaColumn))
grid.Columns[quotaColumn].DefaultCellStyle.Format = "N2";
}
// Imposta l'allineamento al centro per alcune colonne
foreach (var columnName in new[] { "Goals Casa", "Goals Trasferta", "Risultato", "Stato" })
{
if (grid.Columns.Contains(columnName))
grid.Columns[columnName].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
// Colora le righe in base al risultato della previsione
grid.CellFormatting += (sender, e) =>
{
if (e.RowIndex >= 0 && grid.Columns.Contains("Risultato") && e.ColumnIndex >= 0)
{
var resultCell = grid.Rows[e.RowIndex].Cells["Risultato"];
if (resultCell.Value != null)
{
// Se la previsione è corretta, colora di verde
if (Convert.ToInt32(resultCell.Value) == 1)
grid.Rows[e.RowIndex].DefaultCellStyle.BackColor = System.Drawing.Color.LightGreen;
}
}
};
// Abilita il riordino delle colonne
grid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
grid.AllowUserToOrderColumns = true;
}
private string BrowseFolder()
{
using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
{
folderBrowserDialog.Description = "Seleziona la cartella contenente i file CSV delle corse dei cavalli";
folderBrowserDialog.ShowNewFolderButton = false;
return folderBrowserDialog.ShowDialog() == DialogResult.OK ? folderBrowserDialog.SelectedPath : null;
}
}
#region EventHandlers
// Gestore evento per il caricamento dei dati dei cavalli - solo per importazione
private void buttonBrowse_Click(object sender, EventArgs e)
{
// Seleziona cartella con file CSV
string folderPath = BrowseFolder();
if (!string.IsNullOrEmpty(folderPath))
{
// Imposta il percorso nel textbox
textBoxFolderPath.Text = folderPath;
// Abilita il pulsante di importazione
buttonImport.Enabled = true;
}
}
// Replace buttonLoadFootball_Click with these two event handlers:
private void buttonDownloadFootball_Click(object sender, EventArgs e)
{
// Usa direttamente la proprietà Value del DateTimePicker
DateTime selectedDate = dateTimePicker.Value;
_ = DownloadFootballDataAsync(selectedDate);
}
// Gestore evento per il pulsante di importazione dati football
private void buttonImportFootball_Click(object sender, EventArgs e)
{
// Process and import data from frontier table to final tables
_ = ImportFootballDataAsync(dataGridViewFootball);
}
// Nel metodo Main_Load, rimuovi il codice che popolava il ComboBox
private void Main_Load(object sender, EventArgs e)
{
// Imposta la data di ieri per default
dateTimePicker.Value = DateTime.Today.AddDays(-1);
// Inizializza le barre di avanzamento
progressBarHorse.Value = 0;
progressBarFootball.Value = 0;
labelStatusHorse.Text = "Pronto";
labelStatusFootball.Text = "Pronto";
}
// Gestore dell'evento per il pulsante di generazione predizioni
private async void buttonPredict_Click(object sender, EventArgs e)
{
// Inizializza e configura il servizio ML
string connectionString = "Server=DESKTOP-9O9JHFS;Database=TestBS_Horses;User Id=sa;Password=Asti2019;";
mlService.ConnectionString = connectionString;
// Verifica se esiste il modello ML o se deve essere addestrato
string modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Models", "HorseRaceModel.zip");
if (!File.Exists(modelPath))
{
try
{
labelStatusHorse.Text = "Addestramento modello ML in corso...";
await Task.Run(() => mlService.TrainModel(connectionString));
labelStatusHorse.Text = "Addestramento modello ML completato";
}
catch (Exception ex)
{
MessageBox.Show("Errore durante l'addestramento del modello ML:\n" + ex.Message, "Errore ML", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
// Carica il modello ML
try
{
labelStatusHorse.Text = "Caricamento del modello ML...";
mlService.LoadModel();
labelStatusHorse.Text = "Modello ML caricato";
}
catch (Exception ex)
{
MessageBox.Show("Impossibile caricare il modello ML:\n" + ex.Message, "Errore ML", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Genera le predizioni
GeneratePredictions();
}
// Metodo per generare predizioni in base ai dati importati o dal database
private void GeneratePredictions()
{
DataTable workingTable = importedHorsesData;
if (workingTable == null || workingTable.Rows.Count == 0)
{
// Se non ci sono dati importati, carica dal database
workingTable = horseDbManager.GetAllHorseRaceData();
}
if (workingTable == null || workingTable.Rows.Count == 0)
{
MessageBox.Show("Nessun dato disponibile per la predizione.", "Nessun dato", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
// Assicurati che il modello ML sia caricato
if (mlService == null)
{
MessageBox.Show("Errore: servizio ML non inizializzato.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Controlla se la colonna delle previsioni esiste già
if (!workingTable.Columns.Contains("Posizione Prevista"))
workingTable.Columns.Add("Posizione Prevista", typeof(float));
// Copia i dati per lavorare su una nuova tabella
DataTable predictedTable = workingTable.Copy();
// Inizializza la progress bar
progressBarHorse.Minimum = 0;
progressBarHorse.Maximum = predictedTable.Rows.Count;
progressBarHorse.Value = 0;
labelStatusHorse.Text = "Inizializzazione predizioni...";
buttonPredict.Enabled = false;
Task.Run(() =>
{
for (int i = 0; i < predictedTable.Rows.Count; i++)
{
var row = predictedTable.Rows[i];
this.Invoke(new Action(() =>
{
progressBarHorse.Value = i + 1;
labelStatusHorse.Text = $"Generazione predizioni {i + 1} di {predictedTable.Rows.Count}";
}));
try
{
var mlInput = new ModelInput
{
Age = Convert.ToInt32(row["Età"] != DBNull.Value ? row["Età"] : 0),
HandicapRating = 0,
Weight = row["Peso"] != DBNull.Value ? Convert.ToSingle(row["Peso"]) : 0,
WeightCarried = row["Peso"] != DBNull.Value ? Convert.ToSingle(row["Peso"]) : 0,
Barrier = 0,
CareerRuns = 0,
CareerWins = 0,
CareerStrikeRate = 0,
CareerROI = 0,
ThisTrackRuns = 0,
ThisTrackWins = 0,
ThisTrackStrikeRate = 0,
ThisDistanceRuns = 0,
ThisDistanceWins = 0,
ThisDistanceStrikeRate = 0,
JockeyLast100Wins = 0,
JockeyLast100StrikeRate = 0,
TrainerLast100Wins = 0,
TrainerLast100StrikeRate = 0,
BestFixedOdds = row["Quota"] != DBNull.Value ? Convert.ToSingle(row["Quota"]) : 0
};
float? predicted = null;
try
{
var prediction = mlService.PredictFinishPosition(mlInput);
predicted = prediction?.PredictedPosition;
}
catch (Exception ex)
{
Console.WriteLine($"Errore ML.NET: {ex.Message}\n{ex.StackTrace}");
}
row["Posizione Prevista"] = predicted.HasValue ? (object)predicted.Value : DBNull.Value;
}
catch (Exception ex)
{
Console.WriteLine($"Errore durante la predizione per riga {i}: {ex.Message}");
}
}
this.Invoke(new Action(() =>
{
importedHorsesData = predictedTable;
dataGridViewHorse.DataSource = importedHorsesData;
FormatHorseDataGrid(dataGridViewHorse);
ShowStrikeRateStats(predictedTable);
labelStatusHorse.Text = $"Predizioni completate: {predictedTable.Rows.Count} cavalli analizzati";
buttonPredict.Enabled = true;
}));
});
}
catch (Exception ex)
{
MessageBox.Show($"Errore durante la generazione delle predizioni: {ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
labelStatusHorse.Text = "Errore durante la generazione delle predizioni";
buttonPredict.Enabled = true;
}
}
private void buttonImport_Click(object sender, EventArgs e)
{
// Verifica che sia stato selezionato un percorso
if (string.IsNullOrEmpty(textBoxFolderPath.Text))
{
MessageBox.Show("Seleziona prima una cartella contenente i file CSV.",
"Percorso mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Inizia il processo di importazione
ImportCsvFilesHorse(textBoxFolderPath.Text);
}
#endregion
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -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);
}
}
}
}
@@ -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;
}
}
}
}
@@ -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;
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Windows.Forms;
namespace BettingPredictor
{
internal static class Program
{
/// <summary>
/// Punto di ingresso principale dell'applicazione.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Main());
}
}
}
@@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Le informazioni generali relative a un assembly sono controllate dal seguente
// set di attributi. Modificare i valori di questi attributi per modificare le informazioni
// associate a un assembly.
[assembly: AssemblyTitle("BettingPredictor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BettingPredictor")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Se si imposta ComVisible su false, i tipi in questo assembly non saranno visibili
// ai componenti COM. Se è necessario accedere a un tipo in questo assembly da
// COM, impostare su true l'attributo ComVisible per tale tipo.
[assembly: ComVisible(false)]
// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato come ID della libreria dei tipi
[assembly: Guid("63138155-b7f3-4246-b47b-b8cc2d7a60a4")]
// Le informazioni sulla versione di un assembly sono costituite dai seguenti quattro valori:
//
// Versione principale
// Versione secondaria
// Numero di build
// Revisione
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Codice generato da uno strumento.
// Versione runtime:4.0.30319.42000
//
// Le modifiche apportate a questo file possono causare un comportamento non corretto e andranno perse se
// il codice viene rigenerato.
// </auto-generated>
//------------------------------------------------------------------------------
namespace BettingPredictor.Properties
{
/// <summary>
/// Classe di risorse fortemente tipizzata per la ricerca di stringhe localizzate e così via.
/// </summary>
// Questa classe è stata generata automaticamente dalla classe StronglyTypedResourceBuilder
// tramite uno strumento quale ResGen o Visual Studio.
// Per aggiungere o rimuovere un membro, modificare il file .ResX, quindi eseguire di nuovo ResGen
// con l'opzione /str oppure ricompilare il progetto VS.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Restituisce l'istanza di ResourceManager memorizzata nella cache e usata da questa classe.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BettingPredictor.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Esegue l'override della proprietà CurrentUICulture del thread corrente per tutte
/// le ricerche di risorse che utilizzano questa classe di risorse fortemente tipizzata.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace BettingPredictor.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
@@ -0,0 +1,88 @@
/****** Object: StoredProcedure [dbo].[GetFavoriteHorses] Script Date: 17/07/2025 09:09:35 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetFavoriteHorses]
@Date DATETIME = NULL,
@Meeting NVARCHAR(100) = NULL,
@RaceNumber INT = NULL,
@TopN INT = 3,
@Weight_HandicapRating DECIMAL(10,2) = 1,
@Weight_CareerWins DECIMAL(10,2) = 1,
@Weight_CareerStrikeRate DECIMAL(10,2) = 1,
@Weight_JockeyLast100StrikeRate DECIMAL(10,2) = 1,
@Weight_TrainerLast100StrikeRate DECIMAL(10,2) = 1,
@Weight_BestFixedOdds DECIMAL(10,2) = 1,
@Weight_Age DECIMAL(10,2) = 1,
@Weight_CareerPlacings DECIMAL(10,2) = 1,
@Weight_DryTrackStrikeRate DECIMAL(10,2) = 1,
@Weight_ThisTrackStrikeRate DECIMAL(10,2) = 1,
@Weight_LastStartFinishPosition DECIMAL(10,2) = 1,
@Weight_WeightCarried DECIMAL(10,2) = 1
AS
BEGIN
SET NOCOUNT ON;
-- Crea tabella temporanea per i risultati
CREATE TABLE #Results (
DataCorsa NVARCHAR(10),
LuogoCorsa NVARCHAR(100),
NumeroCorsa INT,
NomeCavallo NVARCHAR(100),
Punteggio DECIMAL(18,4),
RisultatoFinale NVARCHAR(50),
PosizioneArrivo INT,
PrevistoVincente BIT
);
WITH Scoring AS (
SELECT
Data,
Meeting,
Race,
HorseName,
-- Calcolo del punteggio pesato con le nuove statistiche
(
ISNULL(HandicapRating,0) * @Weight_HandicapRating +
ISNULL(CareerWins,0) * @Weight_CareerWins +
ISNULL(CareerStrikeRate,0) * @Weight_CareerStrikeRate +
ISNULL(JockeyLast100StrikeRate,0) * @Weight_JockeyLast100StrikeRate +
ISNULL(TrainerLast100StrikeRate,0) * @Weight_TrainerLast100StrikeRate +
ISNULL(BestFixedOdds,0) * @Weight_BestFixedOdds +
ISNULL(Age,0) * @Weight_Age +
ISNULL(CareerPlacings,0) * @Weight_CareerPlacings +
ISNULL(DryTrackStrikeRate,0) * @Weight_DryTrackStrikeRate +
ISNULL(ThisTrackStrikeRate,0) * @Weight_ThisTrackStrikeRate +
-- Per LastStartFinishPosition, valori più bassi sono migliori, quindi invertiamo il segno
(CASE WHEN TRY_CAST(LastStartFinishPosition AS INT) > 0 THEN (1.0 / TRY_CAST(LastStartFinishPosition AS FLOAT)) ELSE 0 END) * @Weight_LastStartFinishPosition +
ISNULL(WeightCarried,0) * @Weight_WeightCarried
) AS Score,
FinishResult
FROM Races
WHERE (FinishResult IS NOT NULL AND FinishResult <> '')
),
Ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Data, Meeting, Race ORDER BY Score DESC) AS RankByScore
FROM Scoring
)
INSERT INTO #Results
SELECT
CONVERT(VARCHAR(10), Data, 120),
Meeting,
Race,
HorseName,
Score,
FinishResult,
CASE WHEN ISNUMERIC(FinishResult) = 1 THEN CAST(FinishResult AS INT) ELSE NULL END,
CASE WHEN RankByScore = 1 THEN 1 ELSE 0 END
FROM Ranked
WHERE RankByScore = 1;
SELECT * FROM #Results;
DROP TABLE #Results;
END
@@ -0,0 +1,27 @@
/****** Object: View [dbo].[GetValidHorses] Script Date: 17/07/2025 09:10:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [dbo].[GetValidHorses]
AS
SELECT Data, Meeting, Race, Num, HorseName, Age, Gender, HandicapRating, CareerRuns, CareerWins, CareerStrikeRate, CareerROI, CareerPlacings, CareerPlaceStrikeRate, DryTrackRuns, DryTrackWins, DryTrackStrikeRate,
DryTrackROI, WetTrackRuns, WetTrackWins, WetTrackStrikeRate, WetTrackROI, AveragePrizeMoney, CareerPrizeMoney, BestFixedOdds, BetEasyOdds, Weight, WeightCarried, Barrier, PrizeMoney, ThisTrackRuns,
ThisTrackWins, ThisTrackStrikeRate, ThisTrackROI, ThisTrackPlaces, ThisTrackPlaceStrikeRate, ThisDistanceRuns, ThisDistanceWins, ThisDistanceStrikeRate, ThisDistanceROI, ThisDistancePlaces,
ThisDistancePlaceStrikeRate, ThisTrackDistanceRuns, ThisTrackDistanceWins, ThisTrackDistanceStrikeRate, ThisTrackDistanceROI, ThisTrackDistancePlaces, ThisTrackDistancePlaceStrikeRate, ThisConditionRuns,
ThisConditionWins, ThisConditionStrikeRate, ThisConditionROI, ThisConditionPlaces, ThisConditionPlaceStrikeRate, Jockey, Apprentice, JockeyWeightClaim, JockeyLast100HorseEarnings, JockeyLast100AvgHorseEarnings,
JockeyLast100Starts, JockeyLast100Wins, JockeyLast100StrikeRate, JockeyLast100ROI, JockeyLast100Places, JockeyLast100PlaceStrikeRate, Jockey12MonthHorseEarnings, Jockey12MonthAvgHorseEarnings,
Jockey12MonthsStarts, Jockey12MonthsWins, Jockey12MonthsStrikeRate, Jockey12MonthsROI, Jockey12MonthsPlaces, Jockey12MonthsPlaceStrikeRate, JockeyThisSeasonHorseEarnings,
JockeyThisSeasonAvgHorseEarnings, JockeyThisSeasonStarts, JockeyThisSeasonWins, JockeyThisSeasonStrikeRate, JockeyThisSeasonROI, JockeyThisSeasonPlaces, JockeyThisSeasonPlaceStrikeRate,
JockeyLastSeasonHorseEarnings, JockeyLastSeasonAvgHorseEarnings, JockeyLastSeasonStarts, JockeyLastSeasonWins, JockeyLastSeasonStrikeRate, JockeyLastSeasonROI, JockeyLastSeasonPlaces,
JockeyLastSeasonPlaceStrikeRate, Trainer, TrainerLast100HorseEarnings, TrainerLast100AvgHorseEarnings, TrainerLast100Starts, TrainerLast100Wins, TrainerLast100StrikeRate, TrainerLast100ROI, TrainerLast100Places,
TrainerLast100PlaceStrikeRate, Trainer12MonthHorseEarnings, Trainer12MonthAvgHorseEarnings, Trainer12MonthsStarts, Trainer12MonthsWins, Trainer12MonthsStrikeRate, Trainer12MonthsROI, Trainer12MonthsPlaces,
Trainer12MonthsPlaceStrikeRate, TrainerThisSeasonHorseEarnings, TrainerThisSeasonAvgHorseEarnings, TrainerThisSeasonStarts, TrainerThisSeasonWins, TrainerThisSeasonStrikeRate, TrainerThisSeasonROI,
TrainerThisSeasonPlaces, TrainerThisSeasonPlaceStrikeRate, TrainerLastSeasonHorseEarnings, TrainerLastSeasonAvgHorseEarnings, TrainerLastSeasonStarts, TrainerLastSeasonWins, TrainerLastSeasonStrikeRate,
TrainerLastSeasonROI, TrainerLastSeasonPlaces, TrainerLastSeasonPlaceStrikeRate, LastStartFinishPosition, LastStartMargin, LastStartDistance, LastStartDistanceChange, LastStartPrizeMoney, FinishResult
FROM dbo.Races
WHERE (FinishResult <> '') OR
(FinishResult IS NOT NULL)
GO
@@ -0,0 +1,20 @@
CREATE TABLE [dbo].[PredictionScanResults] (
ScanId INT IDENTITY(1,1) PRIMARY KEY,
SessionId UNIQUEIDENTIFIER NOT NULL,
ScanDate DATETIME NOT NULL DEFAULT GETDATE(),
Weight_HandicapRating DECIMAL(10,2) NOT NULL,
Weight_CareerWins DECIMAL(10,2) NOT NULL,
Weight_CareerStrikeRate DECIMAL(10,2) NOT NULL,
Weight_JockeyLast100StrikeRate DECIMAL(10,2) NOT NULL,
Weight_TrainerLast100StrikeRate DECIMAL(10,2) NOT NULL,
Weight_BestFixedOdds DECIMAL(10,2) NOT NULL,
Weight_Age DECIMAL(10,2) NOT NULL,
Weight_CareerPlacings DECIMAL(10,2) NOT NULL,
Weight_DryTrackStrikeRate DECIMAL(10,2) NOT NULL,
Weight_ThisTrackStrikeRate DECIMAL(10,2) NOT NULL,
Weight_LastStartFinishPosition DECIMAL(10,2) NOT NULL,
Weight_WeightCarried DECIMAL(10,2) NOT NULL,
TotalRaces INT NOT NULL,
CorrectPredictions INT NOT NULL,
WinPercentage DECIMAL(5,2) NOT NULL
)
@@ -0,0 +1,152 @@
/****** Object: Table [dbo].[Races] Script Date: 16/07/2025 23:27:12 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Races](
[Data] [datetime] NOT NULL,
[Meeting] [nvarchar](100) NOT NULL,
[Race] [int] NOT NULL,
[Num] [int] NOT NULL,
[FileName] [nvarchar](255) NULL,
[HorseName] [nvarchar](100) NULL,
[Age] [int] NULL,
[Gender] [nvarchar](10) NULL,
[HandicapRating] [decimal](10, 2) NULL,
[CareerRuns] [int] NULL,
[CareerWins] [int] NULL,
[CareerStrikeRate] [decimal](10, 2) NULL,
[CareerROI] [decimal](10, 2) NULL,
[CareerPlacings] [int] NULL,
[CareerPlaceStrikeRate] [decimal](10, 2) NULL,
[DryTrackRuns] [int] NULL,
[DryTrackWins] [int] NULL,
[DryTrackStrikeRate] [decimal](10, 2) NULL,
[DryTrackROI] [decimal](10, 2) NULL,
[WetTrackRuns] [int] NULL,
[WetTrackWins] [int] NULL,
[WetTrackStrikeRate] [decimal](10, 2) NULL,
[WetTrackROI] [decimal](10, 2) NULL,
[AveragePrizeMoney] [decimal](15, 2) NULL,
[CareerPrizeMoney] [decimal](15, 2) NULL,
[BestFixedOdds] [decimal](10, 2) NULL,
[BetEasyOdds] [decimal](10, 2) NULL,
[Weight] [decimal](10, 2) NULL,
[WeightCarried] [decimal](10, 2) NULL,
[Barrier] [nvarchar](10) NULL,
[PrizeMoney] [decimal](15, 2) NULL,
[ThisTrackRuns] [int] NULL,
[ThisTrackWins] [int] NULL,
[ThisTrackStrikeRate] [decimal](10, 2) NULL,
[ThisTrackROI] [decimal](10, 2) NULL,
[ThisTrackPlaces] [int] NULL,
[ThisTrackPlaceStrikeRate] [decimal](10, 2) NULL,
[ThisDistanceRuns] [int] NULL,
[ThisDistanceWins] [int] NULL,
[ThisDistanceStrikeRate] [decimal](10, 2) NULL,
[ThisDistanceROI] [decimal](10, 2) NULL,
[ThisDistancePlaces] [int] NULL,
[ThisDistancePlaceStrikeRate] [decimal](10, 2) NULL,
[ThisTrackDistanceRuns] [int] NULL,
[ThisTrackDistanceWins] [int] NULL,
[ThisTrackDistanceStrikeRate] [decimal](10, 2) NULL,
[ThisTrackDistanceROI] [decimal](10, 2) NULL,
[ThisTrackDistancePlaces] [int] NULL,
[ThisTrackDistancePlaceStrikeRate] [decimal](10, 2) NULL,
[ThisConditionRuns] [int] NULL,
[ThisConditionWins] [int] NULL,
[ThisConditionStrikeRate] [decimal](10, 2) NULL,
[ThisConditionROI] [decimal](10, 2) NULL,
[ThisConditionPlaces] [int] NULL,
[ThisConditionPlaceStrikeRate] [decimal](10, 2) NULL,
[Jockey] [nvarchar](100) NULL,
[Apprentice] [nvarchar](10) NULL,
[JockeyWeightClaim] [decimal](10, 2) NULL,
[JockeyLast100HorseEarnings] [decimal](15, 2) NULL,
[JockeyLast100AvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyLast100Starts] [int] NULL,
[JockeyLast100Wins] [int] NULL,
[JockeyLast100StrikeRate] [decimal](10, 2) NULL,
[JockeyLast100ROI] [decimal](10, 2) NULL,
[JockeyLast100Places] [int] NULL,
[JockeyLast100PlaceStrikeRate] [decimal](10, 2) NULL,
[Jockey12MonthHorseEarnings] [decimal](15, 2) NULL,
[Jockey12MonthAvgHorseEarnings] [decimal](15, 2) NULL,
[Jockey12MonthsStarts] [int] NULL,
[Jockey12MonthsWins] [int] NULL,
[Jockey12MonthsStrikeRate] [decimal](10, 2) NULL,
[Jockey12MonthsROI] [decimal](10, 2) NULL,
[Jockey12MonthsPlaces] [int] NULL,
[Jockey12MonthsPlaceStrikeRate] [decimal](10, 2) NULL,
[JockeyThisSeasonHorseEarnings] [decimal](15, 2) NULL,
[JockeyThisSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyThisSeasonStarts] [int] NULL,
[JockeyThisSeasonWins] [int] NULL,
[JockeyThisSeasonStrikeRate] [decimal](10, 2) NULL,
[JockeyThisSeasonROI] [decimal](10, 2) NULL,
[JockeyThisSeasonPlaces] [int] NULL,
[JockeyThisSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[JockeyLastSeasonHorseEarnings] [decimal](15, 2) NULL,
[JockeyLastSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyLastSeasonStarts] [int] NULL,
[JockeyLastSeasonWins] [int] NULL,
[JockeyLastSeasonStrikeRate] [decimal](10, 2) NULL,
[JockeyLastSeasonROI] [decimal](10, 2) NULL,
[JockeyLastSeasonPlaces] [int] NULL,
[JockeyLastSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[Trainer] [nvarchar](100) NULL,
[TrainerLast100HorseEarnings] [decimal](15, 2) NULL,
[TrainerLast100AvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerLast100Starts] [int] NULL,
[TrainerLast100Wins] [int] NULL,
[TrainerLast100StrikeRate] [decimal](10, 2) NULL,
[TrainerLast100ROI] [decimal](10, 2) NULL,
[TrainerLast100Places] [int] NULL,
[TrainerLast100PlaceStrikeRate] [decimal](10, 2) NULL,
[Trainer12MonthHorseEarnings] [decimal](15, 2) NULL,
[Trainer12MonthAvgHorseEarnings] [decimal](15, 2) NULL,
[Trainer12MonthsStarts] [int] NULL,
[Trainer12MonthsWins] [int] NULL,
[Trainer12MonthsStrikeRate] [decimal](10, 2) NULL,
[Trainer12MonthsROI] [decimal](10, 2) NULL,
[Trainer12MonthsPlaces] [int] NULL,
[Trainer12MonthsPlaceStrikeRate] [decimal](10, 2) NULL,
[TrainerThisSeasonHorseEarnings] [decimal](15, 2) NULL,
[TrainerThisSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerThisSeasonStarts] [int] NULL,
[TrainerThisSeasonWins] [int] NULL,
[TrainerThisSeasonStrikeRate] [decimal](10, 2) NULL,
[TrainerThisSeasonROI] [decimal](10, 2) NULL,
[TrainerThisSeasonPlaces] [int] NULL,
[TrainerThisSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[TrainerLastSeasonHorseEarnings] [decimal](15, 2) NULL,
[TrainerLastSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerLastSeasonStarts] [int] NULL,
[TrainerLastSeasonWins] [int] NULL,
[TrainerLastSeasonStrikeRate] [decimal](10, 2) NULL,
[TrainerLastSeasonROI] [decimal](10, 2) NULL,
[TrainerLastSeasonPlaces] [int] NULL,
[TrainerLastSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[LastStartFinishPosition] [nvarchar](10) NULL,
[LastStartMargin] [decimal](10, 2) NULL,
[LastStartDistance] [decimal](10, 2) NULL,
[LastStartDistanceChange] [decimal](10, 2) NULL,
[LastStartPrizeMoney] [decimal](15, 2) NULL,
[FormGuideUrl] [nvarchar](500) NULL,
[HorseProfileUrl] [nvarchar](500) NULL,
[JockeyProfileUrl] [nvarchar](500) NULL,
[TrainerProfileUrl] [nvarchar](500) NULL,
[FinishResult] [nvarchar](50) NULL,
[Created] [datetime] NOT NULL,
[Updated] [datetime] NOT NULL,
CONSTRAINT [PK_Races] PRIMARY KEY CLUSTERED
(
[Data] ASC,
[Meeting] ASC,
[Race] ASC,
[Num] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
@@ -0,0 +1,228 @@
ALTER PROCEDURE [dbo].[ScanFavoriteHorseWeights]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @MinValue INT = 1
DECLARE @MaxValue INT = 3 -- Modifica per range più ampi
DECLARE @SessionId UNIQUEIDENTIFIER = NEWID()
DECLARE @ScanDate DATETIME = GETDATE()
-- Parametri
DECLARE @Weight_HandicapRating INT
DECLARE @Weight_CareerWins INT
DECLARE @Weight_CareerStrikeRate INT
DECLARE @Weight_JockeyLast100StrikeRate INT
DECLARE @Weight_TrainerLast100StrikeRate INT
DECLARE @Weight_BestFixedOdds INT
DECLARE @Weight_Age INT
DECLARE @Weight_CareerPlacings INT
DECLARE @Weight_DryTrackStrikeRate INT
DECLARE @Weight_ThisTrackStrikeRate INT
DECLARE @Weight_LastStartFinishPosition INT
DECLARE @Weight_WeightCarried INT
DECLARE @TotalRaces INT, @CorrectPredictions INT, @WinPercentage DECIMAL(5,2)
DECLARE @CombinazioniTotali BIGINT
DECLARE @CombinazioneCorrente BIGINT = 0
-- Carica tutti i cavalli validi in memoria
IF OBJECT_ID('tempdb..#AllHorses') IS NOT NULL DROP TABLE #AllHorses
SELECT
Data,
Meeting,
Race,
HorseName,
HandicapRating,
CareerWins,
CareerStrikeRate,
JockeyLast100StrikeRate,
TrainerLast100StrikeRate,
BestFixedOdds,
Age,
CareerPlacings,
DryTrackStrikeRate,
ThisTrackStrikeRate,
LastStartFinishPosition,
WeightCarried,
FinishResult
INTO #AllHorses
FROM Races
WHERE (FinishResult IS NOT NULL AND FinishResult <> '')
-- Conta il numero di corse distinte
SELECT @TotalRaces = COUNT(DISTINCT
CONVERT(NVARCHAR(20), Data, 120) + '_' + Meeting + '_' + CAST(Race AS NVARCHAR(10))
)
FROM #AllHorses
-- Calcola il numero totale di combinazioni
SET @CombinazioniTotali = POWER(@MaxValue - @MinValue + 1, 12)
-- Inizializza la sessione con un record "vuoto"
INSERT INTO PredictionScanResults (
SessionId, ScanDate,
Weight_HandicapRating, Weight_CareerWins, Weight_CareerStrikeRate, Weight_JockeyLast100StrikeRate,
Weight_TrainerLast100StrikeRate, Weight_BestFixedOdds, Weight_Age, Weight_CareerPlacings,
Weight_DryTrackStrikeRate, Weight_ThisTrackStrikeRate, Weight_LastStartFinishPosition, Weight_WeightCarried,
TotalRaces, CorrectPredictions, WinPercentage
) VALUES (
@SessionId, @ScanDate,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0
)
SET @Weight_HandicapRating = @MinValue
WHILE @Weight_HandicapRating <= @MaxValue
BEGIN
SET @Weight_CareerWins = @MinValue
WHILE @Weight_CareerWins <= @MaxValue
BEGIN
SET @Weight_CareerStrikeRate = @MinValue
WHILE @Weight_CareerStrikeRate <= @MaxValue
BEGIN
SET @Weight_JockeyLast100StrikeRate = @MinValue
WHILE @Weight_JockeyLast100StrikeRate <= @MaxValue
BEGIN
SET @Weight_TrainerLast100StrikeRate = @MinValue
WHILE @Weight_TrainerLast100StrikeRate <= @MaxValue
BEGIN
SET @Weight_BestFixedOdds = @MinValue
WHILE @Weight_BestFixedOdds <= @MaxValue
BEGIN
SET @Weight_Age = @MinValue
WHILE @Weight_Age <= @MaxValue
BEGIN
SET @Weight_CareerPlacings = @MinValue
WHILE @Weight_CareerPlacings <= @MaxValue
BEGIN
SET @Weight_DryTrackStrikeRate = @MinValue
WHILE @Weight_DryTrackStrikeRate <= @MaxValue
BEGIN
SET @Weight_ThisTrackStrikeRate = @MinValue
WHILE @Weight_ThisTrackStrikeRate <= @MaxValue
BEGIN
SET @Weight_LastStartFinishPosition = @MinValue
WHILE @Weight_LastStartFinishPosition <= @MaxValue
BEGIN
SET @Weight_WeightCarried = @MinValue
WHILE @Weight_WeightCarried <= @MaxValue
BEGIN
SET @CombinazioneCorrente = @CombinazioneCorrente + 1
-- Calcolo in memoria: trova il cavallo con punteggio massimo per ogni corsa
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
SELECT
Data,
Meeting,
Race,
HorseName,
(
ISNULL(HandicapRating,0) * @Weight_HandicapRating +
ISNULL(CareerWins,0) * @Weight_CareerWins +
ISNULL(CareerStrikeRate,0) * @Weight_CareerStrikeRate +
ISNULL(JockeyLast100StrikeRate,0) * @Weight_JockeyLast100StrikeRate +
ISNULL(TrainerLast100StrikeRate,0) * @Weight_TrainerLast100StrikeRate +
ISNULL(BestFixedOdds,0) * @Weight_BestFixedOdds +
ISNULL(Age,0) * @Weight_Age +
ISNULL(CareerPlacings,0) * @Weight_CareerPlacings +
ISNULL(DryTrackStrikeRate,0) * @Weight_DryTrackStrikeRate +
ISNULL(ThisTrackStrikeRate,0) * @Weight_ThisTrackStrikeRate +
(CASE WHEN TRY_CAST(LastStartFinishPosition AS INT) > 0 THEN (1.0 / TRY_CAST(LastStartFinishPosition AS FLOAT)) ELSE 0 END) * @Weight_LastStartFinishPosition +
ISNULL(WeightCarried,0) * @Weight_WeightCarried
) AS Score,
FinishResult
INTO #Results
FROM #AllHorses
-- Seleziona il cavallo con punteggio massimo per ogni corsa
;WITH Ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Data, Meeting, Race ORDER BY Score DESC) AS RankByScore
FROM #Results
)
SELECT *
INTO #TopResults
FROM Ranked
WHERE RankByScore = 1
-- Calcola le statistiche
SELECT
@CorrectPredictions = SUM(CASE WHEN ISNUMERIC(FinishResult) = 1 AND CAST(FinishResult AS INT) = 1 THEN 1 ELSE 0 END)
FROM #TopResults
-- @TotalRaces già calcolato all'inizio
IF @TotalRaces > 0
SET @WinPercentage = CAST(@CorrectPredictions AS DECIMAL(5,2)) * 100.0 / @TotalRaces
ELSE
SET @WinPercentage = 0
-- Aggiorna solo se la percentuale è migliore
IF EXISTS (
SELECT 1 FROM PredictionScanResults
WHERE SessionId = @SessionId
AND WinPercentage < @WinPercentage
)
BEGIN
UPDATE PredictionScanResults
SET
Weight_HandicapRating = @Weight_HandicapRating,
Weight_CareerWins = @Weight_CareerWins,
Weight_CareerStrikeRate = @Weight_CareerStrikeRate,
Weight_JockeyLast100StrikeRate = @Weight_JockeyLast100StrikeRate,
Weight_TrainerLast100StrikeRate = @Weight_TrainerLast100StrikeRate,
Weight_BestFixedOdds = @Weight_BestFixedOdds,
Weight_Age = @Weight_Age,
Weight_CareerPlacings = @Weight_CareerPlacings,
Weight_DryTrackStrikeRate = @Weight_DryTrackStrikeRate,
Weight_ThisTrackStrikeRate = @Weight_ThisTrackStrikeRate,
Weight_LastStartFinishPosition = @Weight_LastStartFinishPosition,
Weight_WeightCarried = @Weight_WeightCarried,
TotalRaces = @TotalRaces,
CorrectPredictions = @CorrectPredictions,
WinPercentage = @WinPercentage
WHERE SessionId = @SessionId
END
-- Output avanzamento
PRINT CONCAT(
'Combinazione ', @CombinazioneCorrente, '/', @CombinazioniTotali,
' (', CAST((@CombinazioneCorrente * 100.0 / @CombinazioniTotali) AS DECIMAL(5,2)), '%) - ',
'Pesi: ', @Weight_HandicapRating, ',', @Weight_CareerWins, ',', @Weight_CareerStrikeRate, ',',
@Weight_JockeyLast100StrikeRate, ',', @Weight_TrainerLast100StrikeRate, ',', @Weight_BestFixedOdds, ',',
@Weight_Age, ',', @Weight_CareerPlacings, ',', @Weight_DryTrackStrikeRate, ',', @Weight_ThisTrackStrikeRate, ',',
@Weight_LastStartFinishPosition, ',', @Weight_WeightCarried,
' - Win%: ', @WinPercentage
)
DROP TABLE IF EXISTS #Results
DROP TABLE IF EXISTS #TopResults
SET @Weight_WeightCarried = @Weight_WeightCarried + 1
END
SET @Weight_LastStartFinishPosition = @Weight_LastStartFinishPosition + 1
END
SET @Weight_ThisTrackStrikeRate = @Weight_ThisTrackStrikeRate + 1
END
SET @Weight_DryTrackStrikeRate = @Weight_DryTrackStrikeRate + 1
END
SET @Weight_CareerPlacings = @Weight_CareerPlacings + 1
END
SET @Weight_Age = @Weight_Age + 1
END
SET @Weight_BestFixedOdds = @Weight_BestFixedOdds + 1
END
SET @Weight_TrainerLast100StrikeRate = @Weight_TrainerLast100StrikeRate + 1
END
SET @Weight_JockeyLast100StrikeRate = @Weight_JockeyLast100StrikeRate + 1
END
SET @Weight_CareerStrikeRate = @Weight_CareerStrikeRate + 1
END
SET @Weight_CareerWins = @Weight_CareerWins + 1
END
SET @Weight_HandicapRating = @Weight_HandicapRating + 1
END
END
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CsvHelper" version="33.0.1" targetFramework="net481" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="Microsoft.Bcl.HashCode" version="6.0.0" targetFramework="net481" />
<package id="Microsoft.Bcl.Numerics" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net481" />
<package id="Microsoft.ML" version="5.0.0-preview.1.25127.4" targetFramework="net481" />
<package id="Microsoft.ML.CpuMath" version="5.0.0-preview.1.25127.4" targetFramework="net481" />
<package id="Microsoft.ML.DataView" version="5.0.0-preview.1.25127.4" targetFramework="net481" />
<package id="Microsoft.ML.FastTree" version="5.0.0-preview.1.25127.4" targetFramework="net481" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net481" />
<package id="System.Buffers" version="4.6.1" targetFramework="net481" />
<package id="System.CodeDom" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="System.Collections.Immutable" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="System.Memory" version="4.6.3" targetFramework="net481" />
<package id="System.Numerics.Tensors" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net481" />
<package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net481" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net481" />
<package id="System.Threading.Channels" version="10.0.0-preview.4.25258.110" targetFramework="net481" />
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net481" />
</packages>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<!-- Pacchetti esistenti -->
<!-- Pacchetti aggiunti per ML.NET -->
<package id="Microsoft.ML" version="2.0.1" targetFramework="net481" />
<package id="Microsoft.ML.CpuMath" version="2.0.1" targetFramework="net481" />
<package id="Microsoft.ML.DataView" version="2.0.1" targetFramework="net481" />
<package id="Microsoft.ML.FastTree" version="2.0.1" targetFramework="net481" />
<package id="System.Buffers" version="4.5.1" targetFramework="net481" />
<package id="System.CodeDom" version="4.7.0" targetFramework="net481" />
<package id="System.Collections.Immutable" version="1.7.1" targetFramework="net481" />
<package id="System.Memory" version="4.5.5" targetFramework="net481" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net481" />
<package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net481" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net481" />
<package id="System.Threading.Channels" version="4.7.1" targetFramework="net481" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net481" />
</packages>