Initial Commit
SuccessfulCode/BritishHorseRacingApi/master There was a failure building this commit

This commit is contained in:
2020-02-22 22:02:44 +00:00
parent 1079ad1e84
commit 6d073c35a5
39 changed files with 1290 additions and 0 deletions
@@ -0,0 +1,133 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Linq;
using Xunit;
using SuccessfulCodeTools.LowLevelHttpClient;
namespace BritishHorseRacingApi.Tests
{
public class ApiClientTests
{
[Fact]
public async Task GetToken()
{
//Arrange
var client = new ApiClient();
//Act
var token = await client.GetNewToken();
//Assert
Assert.False(String.IsNullOrWhiteSpace(token));
}
private const string _userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36";
[Fact]
public async Task Test()
{
//Arrange
var client = new CustomHttpClient(13718);
var message = new CustomHttpRequestMessage(HttpMethod.Get, "http://www.britishhorseracing.com/feeds/v1/token/request");
message.Headers.Add("Accept", "application/json, text/plain, */*");
message.Headers.Add("User-Agent", _userAgent);
message.Headers.Add("Host", "www.britishhorseracing.com");
message.Headers.Add("Connection", "close");
message.Headers.Add("accept-encoding", "gzip, deflate");
var result = await client.SendAsync(message);
Assert.NotNull(result);
}
[Fact]
public async Task GetRaceResult()
{
//Arrange
var client = new ApiClient();
var token = await client.GetNewToken();
//Act
var result = await client.GetRaceResults(2018, 42638, 0);
//Assert
Assert.Equal(6, result.Total);
Assert.Equal(6, result.Data.Length);
Assert.Equal(42638, result.Data[0].RaceId);
}
[Fact]
public async Task GetFixtures()
{
//Arrange
var client = new ApiClient();
var token = await client.GetNewToken();
//Act
var result = await client.GetFixtures(2019, 2);
//Assert
Assert.Equal(111, result.Total);
Assert.Equal(10, result.Data.Length);
Assert.Equal(18142, result.Data[0].FixtureId);
}
[Fact]
public async Task GetAllFixturesForDay()
{
//Arrange
var client = new ApiClient();
var token = await client.GetNewToken();
//Act
var results = await client.GetAllFixturesForDay(2019, 2, 28);
//Assert
Assert.True(results.All(e => e.FixtureDate.Day == 28));
}
[Fact]
public async Task GetAllRacesForDay()
{
var client = new ApiClient();
var token = await client.GetNewToken();
var results = await client.GetAllRacesForDay(2019, 2, 28);
Assert.True(results.Count > 0);
}
[Fact]
public async Task GetRaceRunners()
{
var client = new ApiClient();
var token = await client.GetNewToken();
var results = await client.GetRaceRunners(2020, 50144, 0);
Assert.Equal(13, results.Data.Length);
}
[Fact]
public async Task GetAllRaceResultsForDay()
{
var client = new ApiClient();
var token = await client.GetNewToken();
var results = await client.GetAllRaceResultsForDay(2020, 2, 23);
Assert.True(results.Count > 0);
}
[Fact]
public async Task GetRaces()
{
//Arrange
var client = new ApiClient();
var token = await client.GetNewToken();
//Act
var result = await client.GetRaces(2019, 18188);
//Arrange
Assert.Equal(6, result.Total);
Assert.Equal(6, result.Data.Length);
Assert.Equal(46136, result.Data[0].RaceId);
}
[Fact]
public async Task GetRaceCourse()
{
//Arrange
var client = new ApiClient();
var token = await client.GetNewToken();
//Act
var result = await client.GetRacecourse(18);
//Arrange
Assert.Equal(18, result.CourseId);
}
}
}
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BritishHorseRacingApi\BritishHorseRacingApi.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,39 @@
using BritishHorseRacingApi.Helpers;
using System;
using Xunit;
namespace BritishHorseRacingApi.Tests
{
public class DataConverterHelperTests
{
[Theory()]
[InlineData("1m 24.73s", 1, 24, 73)]
[InlineData("5m 42s", 5, 42, 0)]
[InlineData("7m 2.9s", 7, 2, 9)]
public void TestStopWatchStringConversion(string time, int mins, int seconds, int milliseconds)
{
//Arrange
//Act
var totalMilliseconds = DataConverterHelper.StopWatchStringToMilliseconds(time);
//Assert
var timeSpan = TimeSpan.FromMilliseconds((double)totalMilliseconds);
Assert.Equal(mins, timeSpan.Minutes);
Assert.Equal(seconds, timeSpan.Seconds);
Assert.Equal(milliseconds, timeSpan.Milliseconds);
}
[Theory()]
[InlineData("1m 1f 104y", 2084)]
[InlineData("1f 104y", 324)]
[InlineData("104y", 104)]
public void TestDistanceStringToYardsConversion(string distance, int expectedResult)
{
//Arrange
//Act
var result = DataConverterHelper.DistanceStringToYards(distance);
//Assert
Assert.True(result.HasValue);
Assert.Equal(expectedResult, result.Value);
}
}
}
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29728.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BritishHorseRacingApi", "BritishHorseRacingApi\BritishHorseRacingApi.csproj", "{A8C8795B-EDA8-4AB5-9B85-3DA8E883CBD6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BritishHorseRacingApi.Tests", "BritishHorseRacingApi.Tests\BritishHorseRacingApi.Tests.csproj", "{64B65FA2-BE79-40E5-810D-70C13514A791}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A8C8795B-EDA8-4AB5-9B85-3DA8E883CBD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8C8795B-EDA8-4AB5-9B85-3DA8E883CBD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8C8795B-EDA8-4AB5-9B85-3DA8E883CBD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8C8795B-EDA8-4AB5-9B85-3DA8E883CBD6}.Release|Any CPU.Build.0 = Release|Any CPU
{64B65FA2-BE79-40E5-810D-70C13514A791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64B65FA2-BE79-40E5-810D-70C13514A791}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64B65FA2-BE79-40E5-810D-70C13514A791}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64B65FA2-BE79-40E5-810D-70C13514A791}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7724F108-F427-47FD-87ED-81C538932C80}
EndGlobalSection
EndGlobal
+285
View File
@@ -0,0 +1,285 @@
using BritishHorseRacingApi.Models;
using Newtonsoft.Json.Linq;
using SuccessfulCodeTools;
using SuccessfulCodeTools.LowLevelHttpClient;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
namespace BritishHorseRacingApi
{
public class ApiClient : BaseApiClient
{
private const int _waitTimeAfterFail = 10;
private const string _userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36";
public ApiClient(TimeSpan? delay = null) : base(delay) { }
public static class Endpoints
{
public const string Endpoint = "https://www.britishhorseracing.com/feeds";
private const string V1Endpoint = Endpoint + "/v1";
private const string V3Endpoint = Endpoint + "/v3";
public const string Fixtures = V3Endpoint + "/fixtures";
public const string Races = Fixtures + "/{0}/{1}/races";
public const string Runners = Races + "/races/{0}/{1}/{2}/entries";
public const string RaceResults = V3Endpoint + "/races/{0}/{1}/{2}/results";
public const string Racecourses = V1Endpoint + "/racecourses";
public const string Jockeys = V3Endpoint + "/jockeys";
public const string Animals = V3Endpoint + "/racehorses/{0}";
}
public virtual string CurrentToken { get; set; }
public async Task<string> GetNewToken()
{
await DelayBetweenRequests();
var message = CreateTokenMessage();
var response = await _client.SendAsync(message);
if (!response.IsSuccessStatusCode)
{
throw new Exception("Token not retrieved");
}
var content = response.Content;
while (content.StartsWith("<"))
{
await Task.Delay(TimeSpan.FromMinutes(_waitTimeAfterFail));
//_client.OutgoingPort++;
message = CreateTokenMessage();
response = await _client.SendAsync(message);
content = response.Content;
}
var tokenResponse = content.DeserializeJson<TokenResponse>();
CurrentToken = tokenResponse.Token;
return tokenResponse.Token;
}
private static CustomHttpRequestMessage CreateTokenMessage()
{
var message = new CustomHttpRequestMessage(HttpMethod.Get, "http://www.britishhorseracing.com/feeds/v1/token/request");
message.Headers.Add("Accept", "application/json, text/plain, */*");
message.Headers.Add("Accept-Language", "en-US,en;q=0.9");
message.Headers.Add("User-Agent", _userAgent);
message.Headers.Add("Host", "www.britishhorseracing.com");
message.Headers.Add("cache-control", "no-cache");
message.Headers.Add("Postman-Token", "887efa5f-fe8e-4ce5-bfc1-810789691391");
message.Headers.Add("Connection", "close");
message.Headers.Add("accept-encoding", "gzip, deflate");
return message;
}
public async Task<List<Race>> GetAllRacesForDay(int year, int month, int day)
{
var fixtures = await GetAllFixturesForDay(year, month, day);
var races = new List<Race>();
foreach(var fixture in fixtures)
{
var page = await GetRaces(year, fixture.FixtureId);
races.AddRange(page.Data);
}
return races;
}
public async Task<List<RunnerResult>> GetAllRaceResultsForDay(int year, int month, int day)
{
var races = await GetAllRacesForDay(year, month, day);
var runnerResults = new List<RunnerResult>();
foreach (var race in races)
{
var page = await GetRaceResults(year, race.RaceId, race.DivisionSequence);
runnerResults.AddRange(page.Data);
}
return runnerResults;
}
public async Task<PagedResult<JockeySummary>> GetJockeys(int page = 1, int perPage = 2)
{
var uriBuilder = new UriBuilder(Endpoints.Jockeys)
{
Port = -1
};
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query["page"] = page.ToString();
query["per_page"] = perPage.ToString();
uriBuilder.Query = query.ToString();
var results = await ExecuteRequestForPagedResults<JockeySummary>(uriBuilder.ToString());
return results;
}
public async Task<Jockey> GetJockey(int id)
{
var result = await ExecuteRequest<SingleResult<Jockey>>(Endpoints.Jockeys + $"/{id}", (token) => {
return !(token["data"] is JArray);
});
if(result == null)
{
return null;
}
return result.Data;
}
public async Task<PagedResult<RunnerResult>> GetRaceResults(int year, int raceId, int divisionSequence = 0)
{
var requestUrl = String.Format(Endpoints.RaceResults, year, raceId, divisionSequence);
return await ExecuteRequestForPagedResults<RunnerResult>(requestUrl);
}
public async Task<PagedResult<Runner>> GetRaceRunners(int year, int raceId, int divisionSequence = 0)
{
var requestUrl = String.Format(Endpoints.Runners, year, raceId, divisionSequence);
return await ExecuteRequestForPagedResults<Runner>(requestUrl);
}
public async Task<PagedResult<Racecourse>> GetRacecourses()
{
return await ExecuteRequestForPagedResults<Racecourse>(Endpoints.Racecourses);
}
public async Task<Racecourse> GetRacecourse(int id)
{
var result = await ExecuteRequest<SingleResult<Racecourse>>(Endpoints.Racecourses + "/" + id);
return result.Data;
}
public async Task<PagedResult<Race>> GetRaces(int year, int fixtureId)
{
var finalUrl = String.Format(Endpoints.Races, year, fixtureId);
return await ExecuteRequestForPagedResults<Race>(finalUrl);
}
public async Task<Animal> GetAnimal(int id)
{
var endpoint = String.Format(Endpoints.Animals, id);
var result = await ExecuteRequest<SingleResult<Animal>>(endpoint, (token) => !(token["data"] is JArray));
if (result == null)
{
return null;
}
return result.Data;
}
public async Task<List<Fixture>> GetAllFixturesForDay(int year, int month, int day)
{
var fixtures = new List<Fixture>();
var pages = 1;
for(var i = 1; i <= pages; i++)
{
var resultsPage = await GetFixtures(year, month, i);
foreach(var item in resultsPage.Data)
{
//Items returned day descending, skip until relevant day
if (item.FixtureDate.Day > day)
continue;
//If we hit another day dont bother getting more
if (item.FixtureDate.Day < day)
return fixtures;
fixtures.Add(item);
}
if (resultsPage.CurrentPage == resultsPage.LastPage)
return fixtures;
pages++;
}
return fixtures;
}
public async Task<PagedResult<Fixture>> GetFixtures(int? year = null, int? month = null, int page = 1, int perPage = 10, string order = "desc", bool? resultsAvailable = true)
{
var uriBuilder = new UriBuilder(Endpoints.Fixtures);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query["page"] = page.ToString();
query["per_page"] = perPage.ToString();
query["order"] = order;
if (year != null)
{
query["year"] = year.ToString();
}
if(month != null)
{
query["month"] = month.ToString();
}
if(resultsAvailable != null)
{
query["resultAvailable"] = resultsAvailable.Value.ToString();
}
uriBuilder.Query = query.ToString();
var finalUrl = uriBuilder.ToString();
return await ExecuteRequestForPagedResults<Fixture>(finalUrl);
}
private Task<PagedResult<T>> ExecuteRequestForPagedResults<T>(string endpoint)
{
return ExecuteRequest<PagedResult<T>>(endpoint);
}
private async Task<T> ExecuteRequest<T>(string endpoint, Func<JToken, bool> preDeserilizationValidation = null)
{
var response = await SendRequest(endpoint);
if(response.StatusCode == HttpStatusCode.Unauthorized)
{
await GetNewToken();
response = await SendRequest(endpoint);
}
var content = response.Content;
while (content.StartsWith("<"))
{
await Task.Delay(TimeSpan.FromMinutes(_waitTimeAfterFail));
//_client.OutgoingPort++;
await GetNewToken();
response = await SendRequest(endpoint);
content = response.Content;
}
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Unexpected error: {content}" );
}
if(preDeserilizationValidation != null && !preDeserilizationValidation(JToken.Parse(content)))
{
return default(T);
}
var deserializedResponse = content.DeserializeJson<T>();
return deserializedResponse;
}
protected override async Task PresendRequestAlterations(CustomHttpRequestMessage message)
{
message.Headers.Add("User-Agent", _userAgent);
if (CurrentToken == null)
{
await GetNewToken();
}
//message.Headers.TryAddWithoutValidation("Authorization", CurrentToken);
message.Headers.Add("Authorization", CurrentToken);
message.Headers.Add("Accept", "application/json");
message.Headers.Add("accept-encoding", "gzip, deflate");
message.Headers.Add("Accept-Language", "en-US,en;q=0.9");
}
}
}
+55
View File
@@ -0,0 +1,55 @@
using SuccessfulCodeTools.LowLevelHttpClient;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace BritishHorseRacingApi
{
public abstract class BaseApiClient
{
private const int _startPort = 55948;
protected readonly CustomHttpClient _client = new CustomHttpClient(_startPort);
private readonly TimeSpan? _delay;
private DateTime _lastRequest = DateTime.UtcNow;
public BaseApiClient(TimeSpan? delay = null)
{
_delay = delay;
_client.DefaultHeaders.Add("Cookie", "bha-ie-test=true; D_SID=188.172.158.138:JT/OEx87nl5ScspTQk38UsDfuIz94DbCf5CYsIC7WpU; _ga=GA1.2.578018316.1549102711; __utmz=36595059.1549187117.8.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); __utma=36595059.578018316.1549102711.1550365414.1551216842.16; __utmc=36595059; D_IID=80C3F85C-C90F-31DF-8830-AF181B9C7A2E; D_UID=33DA8B87-0FB7-3D76-9DA6-A650196A2B16; D_ZID=68EA5585-B6A0-34F7-939A-A0C1B40036B3; D_ZUID=0E647F3A-6570-32D2-9673-8B49FFEBE647; D_HID=EBE66F1F-FA2C-3373-8163-110008C50265");
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
}
protected async Task DelayBetweenRequests()
{
if (!_delay.HasValue)
{
_lastRequest = DateTime.UtcNow;
return;
}
var timeSinceLastRequest = DateTime.UtcNow - _lastRequest;
var timeToWait = _delay.Value - timeSinceLastRequest;
if (timeToWait.TotalMilliseconds > 0)
{
await Task.Delay(timeToWait);
}
_lastRequest = DateTime.UtcNow;
}
protected async Task<CustomHttpResponseMessage> SendRequest(string endpoint)
{
await DelayBetweenRequests();
var message = new CustomHttpRequestMessage(HttpMethod.Get, endpoint);
message.Headers.Add("Accept", "application/json, text/plain, */*");
await PresendRequestAlterations(message);
var response = await _client.SendAsync(message);
return response;
}
protected abstract Task PresendRequestAlterations(CustomHttpRequestMessage message);
}
}
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SuccessfulCodeTools" Version="1.0.2" />
</ItemGroup>
</Project>
@@ -0,0 +1,28 @@
using Newtonsoft.Json;
using System;
namespace BritishHorseRacingApi
{
public class FalseOrObjectConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((bool)value) ? 1 : 0);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if(reader.Value != null)
{
return null;
}
return serializer.Deserialize(reader, objectType);
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
}
@@ -0,0 +1,66 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace BritishHorseRacingApi.Helpers
{
public static class DataConverterHelper
{
private static Regex _stopwatchTimeRegex = new Regex(@"^(?'minutes'[0-9]*(?=m))?m? ?(?'seconds'[0-9\.]*)s ?(?:\(\+)?(?'lossDifference'[0-9\.]*)(?:s\))?", RegexOptions.Compiled);
public static int? StopWatchStringToMilliseconds(string stopwatchValue)
{
var match = _stopwatchTimeRegex.Match(stopwatchValue);
var minutes = GetComponent(match, "minutes", 0);
var secondsAndMilliseconds = GetComponent(match, "seconds", "0").Split('.');
var seconds = int.Parse(secondsAndMilliseconds[0]);
var milliseconds = secondsAndMilliseconds.Length > 1 ? int.Parse(secondsAndMilliseconds[1]) : 0;
var timespan = new TimeSpan(0, 0, minutes, seconds, milliseconds);
return (int)timespan.TotalMilliseconds;
}
private static Regex _distanceExtractorRegex = new Regex(BuildExtractComponentsRegex(
("miles", "m"),
("furlongs", "f"),
("yards", "y")), RegexOptions.Compiled);
public static int? DistanceStringToYards(string distance)
{
if (String.IsNullOrWhiteSpace(distance))
return null;
var match = _distanceExtractorRegex.Match(distance);
var miles = GetComponent(match, "miles", 0);
var furlongs = GetComponent(match, "furlongs", 0);
var yards = GetComponent(match, "yards", 0);
var totalYards = (miles * 1760) + (furlongs * 220) + yards;
return totalYards;
}
private static string BuildExtractComponentsRegex(params (string label, string indicator)[] pairs)
{
var sb = new StringBuilder();
Array.ForEach(pairs, e => sb.AppendFormat(@"(?'{0}'[0-9\.]*(?={1}))?{1}? ?", e.label, e.indicator));
return sb.ToString();
}
private static T GetComponent<T>(Match match, string component, T defaultValue = default)
{
var group = match.Groups[component];
if (String.IsNullOrWhiteSpace(group.Value))
return defaultValue;
return (T)Convert.ChangeType(group.Value, typeof(T));
}
public static int StopwatchStringDifferenceToMilliseconds(string timeDifferenceString)
{
var match = _stopwatchTimeRegex.Match(timeDifferenceString);
var lossDifference = GetComponent(match, "lossDifference", 0);
return (int)TimeSpan.FromSeconds(lossDifference).TotalMilliseconds;
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
namespace BritishHorseRacingApi.Models
{
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public DateTime? DeathDate { get; set; }
public string DeathNote { get; set; }
public string Moniker { get; set; }
public string Sex { get; set; }
public string TrainerName { get; set; }
public int? TrainerId { get; set; }
public string OwnerName { get; set; }
public int? OwnerId { get; set; }
public decimal? RatingAwt { get; set; }
public decimal? RatingChase { get; set; }
public decimal? RatingFlat { get; set; }
public decimal? RatingHurdle { get; set; }
public string NameWithNoCountry { get; set; }
public PerformanceDetail[] PerformanceDetails { get; set; }
public Lineage Lineage { get; set; }
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace BritishHorseRacingApi.Models
{
public partial class Career
{
public int Wins { get; set; }
public int Rides { get; set; }
public decimal WinsToRides { get; set; }
public int GroupListedWins { get; set; }
public decimal PrizeMoney { get; set; }
}
}
+5
View File
@@ -0,0 +1,5 @@
namespace BritishHorseRacingApi.Models
{
public enum Country { Uk };
}
+39
View File
@@ -0,0 +1,39 @@
using System;
namespace BritishHorseRacingApi.Models
{
public class Fixture
{
public int FixtureId { get; set; }
public int FixtureYear { get; set; }
public DateTime FixtureDate { get; set; }
public string Distance { get; set; }
public string CourseName { get; set; }
public DateTime? FirstRace { get; set; }
public bool ResultsAvailable { get; set; }
public bool RacecardAvailable { get; set; }
public bool EntriesAvailable { get; set; }
public int BlackTypeRaces { get; set; }
public int BcsEvent { get; set; }
public int NumberOfRaces { get; set; }
public string FixtureName { get; set; }
public FixtureType FixtureType { get; set; }
public int MeetingId { get; set; }
public RacingTrackType RacingTrackType { get; set; }
public RacePlanningCode RacePlanningCode { get; set; }
public int CourseId { get; set; }
public string TicketsLink { get; set; }
public int? MajorEvent { get; set; }
public string Going { get; set; }
public string Weather { get; set; }
public string Other { get; set; }
public string Stalls { get; set; }
public string Rail { get; set; }
public int AbandonedReasonCode { get; set; }
public FixtureSession FixtureSession { get; set; }
public string GoingUpdatedDate { get; set; }
public string GoingUpdatedTime { get; set; }
public Official[] Officials { get; set; }
public NonRunner[] NonRunners { get; set; }
}
}
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum FixtureSession { Afternoon, Evening, Floodlit };
}
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum FixtureType { Flat, Jump, Mixed };
}
+19
View File
@@ -0,0 +1,19 @@
using System;
namespace BritishHorseRacingApi.Models
{
public class Jockey
{
public int Id { get; set; }
public string Name { get; set; }
public string County { get; set; }
public DateTimeOffset DateOfBirth { get; set; }
public int Age { get; set; }
public int LowestRidingWeight { get; set; }
public int? DaysSinceLastWin { get; set; }
public string LicenseType { get; set; }
public string JockeyImage { get; set; }
public Career Career { get; set; }
public RecentPerformance[] RecentPerformances { get; set; }
}
}
@@ -0,0 +1,13 @@
namespace BritishHorseRacingApi.Models
{
public class JockeySummary
{
public int Id { get; set; }
public string Name { get; set; }
public string County { get; set; }
public int Age { get; set; }
public int? DaysSinceLastWin { get; set; }
public string LicenseType { get; set; }
public string JockeyImage { get; set; }
}
}
+13
View File
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public partial class LastRun
{
[JsonProperty("raceType")]
public string RaceType { get; set; }
[JsonProperty("days")]
public int Days { get; set; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public class Lineage
{
[JsonConverter(typeof(FalseOrObjectConverter))]
public LineageAnimal Dam { get; set; }
[JsonConverter(typeof(FalseOrObjectConverter))]
public LineageAnimal Sire { get; set; }
}
}
@@ -0,0 +1,9 @@
namespace BritishHorseRacingApi.Models
{
public class LineageAnimal
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
}
}
+41
View File
@@ -0,0 +1,41 @@
using Newtonsoft.Json;
using System;
namespace BritishHorseRacingApi.Models
{
public class NonRunner
{
[JsonProperty("fixtureId")]
public long FixtureId { get; set; }
[JsonProperty("fixtureYear")]
public int FixtureYear { get; set; }
[JsonProperty("raceId")]
public long RaceId { get; set; }
[JsonProperty("divisionSequence")]
public long DivisionSequence { get; set; }
[JsonProperty("raceName")]
public string RaceName { get; set; }
[JsonProperty("horseName")]
public string HorseName { get; set; }
[JsonProperty("fixtureDate")]
public DateTime FixtureDate { get; set; }
[JsonProperty("raceTime")]
public TimeSpan RaceTime { get; set; }
[JsonProperty("declaredDate")]
public DateTime? DeclaredDate { get; set; }
[JsonProperty("declaredTime")]
public TimeSpan DeclaredTime { get; set; }
[JsonProperty("declaredReason")]
public string DeclaredReason { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace BritishHorseRacingApi.Models
{
public partial class Official
{
public string Category { get; set; }
public string[] Officials { get; set; }
}
}
@@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public class PagedResult<T>
{
public int Total { get; set; }
[JsonProperty("per_page")]
public int PerPage { get; set; }
[JsonProperty("current_page")]
public int CurrentPage { get; set; }
[JsonProperty("last_page")]
public int LastPage { get; set; }
public int From { get; set; }
public int To { get; set; }
public T[] Data { get; set; }
}
}
@@ -0,0 +1,11 @@
namespace BritishHorseRacingApi.Models
{
public class PerformanceDetail
{
public int TotalWins { get; set; }
public int TotalRuns { get; set; }
public int TotalPlaces { get; set; }
public decimal? TotalPrizeMoney { get; set; }
}
}
+36
View File
@@ -0,0 +1,36 @@
using BritishHorseRacingApi.Helpers;
using System;
using System.Text.RegularExpressions;
namespace BritishHorseRacingApi.Models
{
public class Race
{
public int RaceId { get; set; }
public int YearOfRace { get; set; }
public int DivisionSequence { get; set; }
public DateTime RaceDate { get; set; }
public TimeSpan RaceTime { get; set; }
public string RaceName { get; set; }
public string AgeLimit { get; set; }
public int PrizeAmount { get; set; }
public string PrizeCurrency { get; set; }
public int RaceClass { get; set; }
public string RatingBand { get; set; }
public string RawDistanceText { get; set; }
public string DistanceUnits { get; set; }
public int DistanceValue { get; set; }
public string DistanceText { get; set; }
public string DistanceChange { get; set; }
public string RaceCriteriaRaceType { get; set; }
public int AbandonedReasonCode { get; set; }
public int BlackTypeRace { get; set; }
public string DistanceChangeText { get; set; }
public bool Plus10 { get; set; }
public WinnersDetail[] WinnersDetails { get; set; }
public Regex _distanceExtractorRegex = new Regex(@"(?'miles'[0-9\.]*(?=m))?m? ?(?'furlongs'[0-9\.]*(?=f))?f? ?.*?(?'yards'[0-9\.]*(?=y))", RegexOptions.Compiled);
public int? DistanceInYards() => DataConverterHelper.DistanceStringToYards(DistanceText);
}
}
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum RacePlanningCode { Normal };
}
@@ -0,0 +1,29 @@
using Newtonsoft.Json;
using System;
namespace BritishHorseRacingApi.Models
{
public class Racecourse
{
public int CourseId { get; set; }
public string Name { get; set; }
[JsonProperty("Type")]
public TrackStyle TrackStyle { get; set; }
public Region Region { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Web { get; set; }
public string Logo { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string Address4 { get; set; }
public string Address5 { get; set; }
public string Postcode { get; set; }
public Country? Country { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public DateTime? FirstRace { get; set; }
public DateTime NextFixtureDate { get; set; }
}
}
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum RacingTrackType { Awt, Turf, Mixed };
}
@@ -0,0 +1,37 @@
using System;
namespace BritishHorseRacingApi.Models
{
public class RecentPerformance
{
public int? RaceId { get; set; }
public int? YearOfRace { get; set; }
public int? DivisionSequence { get; set; }
public int? CourseId { get; set; }
public string CourseName { get; set; }
public int? RaceRunners { get; set; }
public int? ResultPosition { get; set; }
public string BettingRatio { get; set; }
public int? AnimalId { get; set; }
public string AnimalName { get; set; }
public int? JockeyId { get; set; }
public string JockeyName { get; set; }
public int? WeightCarried { get; set; }
public int? TrainerId { get; set; }
public string TrainerName { get; set; }
public DateTimeOffset? RaceDateTime { get; set; }
public DateTimeOffset? RaceDate { get; set; }
public DateTimeOffset? RaceTime { get; set; }
public int? RaceClass { get; set; }
public TrackStyle RaceType { get; set; }
public string RaceDistanceText { get; set; }
public string RaceName { get; set; }
public string PrizeMoney { get; set; }
public string Going { get; set; }
public int? FlatRating { get; set; }
public object AwtRating { get; set; }
public object ChaseRating { get; set; }
public object HurdleRating { get; set; }
public Status Status { get; set; }
}
}
+4
View File
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum Region { Midlands, North, South };
}
+139
View File
@@ -0,0 +1,139 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public partial class Runner
{
[JsonProperty("raceId")]
public int RaceId { get; set; }
[JsonProperty("yearOfRace")]
public int YearOfRace { get; set; }
[JsonProperty("divisionSequence")]
public int DivisionSequence { get; set; }
[JsonProperty("animalId")]
public int AnimalId { get; set; }
[JsonProperty("racehorseName")]
public string RacehorseName { get; set; }
[JsonProperty("ageYears")]
public int AgeYears { get; set; }
[JsonProperty("bred")]
public string Bred { get; set; }
[JsonProperty("clothNumber")]
public int ClothNumber { get; set; }
[JsonProperty("drawnStall")]
public int? DrawnStall { get; set; }
[JsonProperty("reserveNumber")]
public string ReserveNumber { get; set; }
[JsonProperty("bhaRating")]
public string BhaRating { get; set; }
[JsonProperty("formFigures")]
public string FormFigures { get; set; }
[JsonProperty("weightValue")]
public string WeightValue { get; set; }
[JsonProperty("penaltyValue")]
public int PenaltyValue { get; set; }
[JsonProperty("sexType")]
public SexType SexType { get; set; }
[JsonProperty("nonRunnerDeclaredReason")]
public string NonRunnerDeclaredReason { get; set; }
[JsonProperty("nonRunnerDeclaredDate")]
public string NonRunnerDeclaredDate { get; set; }
[JsonProperty("nonRunnerDeclaredTime")]
public string NonRunnerDeclaredTime { get; set; }
[JsonProperty("runner")]
public int RunnerNumber { get; set; }
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("jockeyId")]
public int JockeyId { get; set; }
[JsonProperty("jockeyName")]
public string JockeyName { get; set; }
[JsonProperty("weightsJockeyClaiming")]
public int WeightsJockeyClaiming { get; set; }
[JsonProperty("headGearWearingCode")]
public string HeadGearWearingCode { get; set; }
[JsonProperty("headGearWearingSheepSkin")]
public int HeadGearWearingSheepSkin { get; set; }
[JsonProperty("headGearDescription")]
public string HeadGearDescription { get; set; }
[JsonProperty("headGearAbbreviation")]
public string HeadGearAbbreviation { get; set; }
[JsonProperty("jockeyProfile")]
public int JockeyProfile { get; set; }
[JsonProperty("courseWinner")]
public string CourseWinner { get; set; }
[JsonProperty("distanceWinner")]
public string DistanceWinner { get; set; }
[JsonProperty("windSurgeryFirstRun")]
public string WindSurgeryFirstRun { get; set; }
[JsonProperty("trainerId")]
public int TrainerId { get; set; }
[JsonProperty("trainerName")]
public string TrainerName { get; set; }
[JsonProperty("ownerId")]
public int OwnerId { get; set; }
[JsonProperty("ownerName")]
public string OwnerName { get; set; }
[JsonProperty("wbSilkCode")]
public string WbSilkCode { get; set; }
[JsonProperty("wbSilkDescription")]
public string WbSilkDescription { get; set; }
[JsonProperty("silkImage")]
public string SilkImage { get; set; }
[JsonProperty("plus10")]
public int Plus10 { get; set; }
[JsonProperty("forecastPriceNumerator")]
public string ForecastPriceNumerator { get; set; }
[JsonProperty("forecastPriceDenominator")]
public string ForecastPriceDenominator { get; set; }
[JsonProperty("weightText")]
public string WeightText { get; set; }
[JsonProperty("odds")]
public string Odds { get; set; }
[JsonProperty("lastRuns")]
public LastRun[] LastRuns { get; set; }
}
}
@@ -0,0 +1,55 @@
using BritishHorseRacingApi.Helpers;
using System;
using System.Text.RegularExpressions;
namespace BritishHorseRacingApi.Models
{
public class RunnerResult
{
public int RaceId { get; set; }
public int YearOfRace { get; set; }
public int DivisionSequence { get; set; }
public int HorseId { get; set; }
public int AnimalId { get; set; }
public int? ResultFinishPos { get; set; }
public string Status { get; set; }
public int Ran { get; set; }
public string NonRunnerDeclaredReason { get; set; }
public DateTime? NonRunnerDeclaredDate { get; set; }
public TimeSpan? NonRunnerDeclaredTime { get; set; }
public string RacehorseName { get; set; }
public int AgeYear { get; set; }
public string Bred { get; set; }
public int? ClothNumber { get; set; }
public int? DrawnStall { get; set; }
public int? WeightValue { get; set; }
public string SexType { get; set; }
public int? JockeyId { get; set; }
public string JockeyName { get; set; }
public int JockeyProfile { get; set; }
public int? TrainerId { get; set; }
public string TrainerName { get; set; }
public int? OwnerId { get; set; }
public string OwnerName { get; set; }
public string WbSilkCode { get; set; }
public string WbSilkDescription { get; set; }
public string SilkImage { get; set; }
public int Plus10 { get; set; }
public string ResultBtnDistance { get; set; }
public string PositionFinishTime { get; set; }
public string BettingRatio { get; set; }
public string PerformanceFigure { get; set; }
public int? RatingFlat { get; set; }
public string RatingAwt { get; set; }
public string RatingChase { get; set; }
public string RatingHurdle { get; set; }
public string RaceCriteriaRaceType { get; set; }
public int Hro { get; set; }
public string FinishTime { get; set; }
public int? FinishTimeInMilliseconds() => DataConverterHelper.StopWatchStringToMilliseconds(FinishTime);
public int LossMilliseconds() => DataConverterHelper.StopwatchStringDifferenceToMilliseconds(FinishTime);
public string RacehorseNameWithoutConuntry => RacehorseName.Split('(')[0].Trim();
}
}
+4
View File
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum SexType { Filly, Gelding };
}
@@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public class SingleResult<T>
{
public T Data { get; set; }
}
}
+4
View File
@@ -0,0 +1,4 @@
namespace BritishHorseRacingApi.Models
{
public enum Status { Runner, Dnf, Withdrawn };
}
@@ -0,0 +1,11 @@
using Newtonsoft.Json;
namespace BritishHorseRacingApi.Models
{
public class TokenResponse
{
[JsonProperty("token")]
public string Token { get; set; }
public string Session { get; set; }
}
}
@@ -0,0 +1,5 @@
namespace BritishHorseRacingApi.Models
{
public enum TrackStyle { Flat, Jump, Mixed };
}
@@ -0,0 +1,14 @@
using System;
namespace BritishHorseRacingApi.Models
{
public class WinnersDetail
{
public int Position { get; set; }
public string JockeyName { get; set; }
public string Trainername { get; set; }
public Uri SilkImage { get; set; }
public string RacehorseName { get; set; }
}
}
Vendored
+37
View File
@@ -0,0 +1,37 @@
pipeline
{
agent { dockerfile true }
stages
{
stage('Build')
{
steps
{
sh 'dotnet restore'
sh 'dotnet build -c Release --no-restore'
sh "dotnet pack -c Release --no-restore --no-build -p:PackageVersion=1.0.${env.BUILD_NUMBER}"
}
}
stage('Archive artifacts')
{
steps
{
archiveArtifacts artifacts:'*/bin/Release/**/*.nupkg'
}
}
stage('Publish nuget artifacts')
{
steps
{
sh "./JenkinsfileScripts/PublishNugetArtifacts.sh ${env.BUILD_NUMBER}"
}
}
}
post {
always {
deleteDir() /* clean up our workspace */
}
}
}