Allineamento dati con repository di origine
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user