From 6d073c35a5d19ff940e49f32f3e659b1b90a5ebd Mon Sep 17 00:00:00 2001 From: Martin Lee Date: Sat, 22 Feb 2020 22:02:44 +0000 Subject: [PATCH] Initial Commit --- BritishHorseRacingApi.Tests/ApiClientTests.cs | 133 ++++++++ .../BritishHorseRacingApi.Tests.csproj | 15 + .../DataConverterHelperTests.cs | 39 +++ BritishHorseRacingApi.sln | 31 ++ BritishHorseRacingApi/ApiClient.cs | 285 ++++++++++++++++++ BritishHorseRacingApi/BaseApiClient.cs | 55 ++++ .../BritishHorseRacingApi.csproj | 11 + .../FalseOrObjectConverter.cs | 28 ++ .../Helpers/DataConverterHelper.cs | 66 ++++ BritishHorseRacingApi/Models/Animal.cs | 27 ++ BritishHorseRacingApi/Models/Career.cs | 11 + BritishHorseRacingApi/Models/Country.cs | 5 + BritishHorseRacingApi/Models/Fixture.cs | 39 +++ .../Models/FixtureSession.cs | 4 + BritishHorseRacingApi/Models/FixtureType.cs | 4 + BritishHorseRacingApi/Models/Jockey.cs | 19 ++ BritishHorseRacingApi/Models/JockeySummary.cs | 13 + BritishHorseRacingApi/Models/LastRun.cs | 13 + BritishHorseRacingApi/Models/Lineage.cs | 12 + BritishHorseRacingApi/Models/LineageAnimal.cs | 9 + BritishHorseRacingApi/Models/NonRunner.cs | 41 +++ BritishHorseRacingApi/Models/Official.cs | 8 + BritishHorseRacingApi/Models/PagedResult.cs | 18 ++ .../Models/PerformanceDetail.cs | 11 + BritishHorseRacingApi/Models/Race.cs | 36 +++ .../Models/RacePlanningCode.cs | 4 + BritishHorseRacingApi/Models/Racecourse.cs | 29 ++ .../Models/RacingTrackType.cs | 4 + .../Models/RecentPerformance.cs | 37 +++ BritishHorseRacingApi/Models/Region.cs | 4 + BritishHorseRacingApi/Models/Runner.cs | 139 +++++++++ BritishHorseRacingApi/Models/RunnerResult.cs | 55 ++++ BritishHorseRacingApi/Models/SexType.cs | 4 + BritishHorseRacingApi/Models/SingleResult.cs | 10 + BritishHorseRacingApi/Models/Status.cs | 4 + BritishHorseRacingApi/Models/TokenResponse.cs | 11 + BritishHorseRacingApi/Models/TrackStyle.cs | 5 + BritishHorseRacingApi/Models/WinnersDetail.cs | 14 + Jenkinsfile | 37 +++ 39 files changed, 1290 insertions(+) create mode 100644 BritishHorseRacingApi.Tests/ApiClientTests.cs create mode 100644 BritishHorseRacingApi.Tests/BritishHorseRacingApi.Tests.csproj create mode 100644 BritishHorseRacingApi.Tests/DataConverterHelperTests.cs create mode 100644 BritishHorseRacingApi.sln create mode 100644 BritishHorseRacingApi/ApiClient.cs create mode 100644 BritishHorseRacingApi/BaseApiClient.cs create mode 100644 BritishHorseRacingApi/BritishHorseRacingApi.csproj create mode 100644 BritishHorseRacingApi/FalseOrObjectConverter.cs create mode 100644 BritishHorseRacingApi/Helpers/DataConverterHelper.cs create mode 100644 BritishHorseRacingApi/Models/Animal.cs create mode 100644 BritishHorseRacingApi/Models/Career.cs create mode 100644 BritishHorseRacingApi/Models/Country.cs create mode 100644 BritishHorseRacingApi/Models/Fixture.cs create mode 100644 BritishHorseRacingApi/Models/FixtureSession.cs create mode 100644 BritishHorseRacingApi/Models/FixtureType.cs create mode 100644 BritishHorseRacingApi/Models/Jockey.cs create mode 100644 BritishHorseRacingApi/Models/JockeySummary.cs create mode 100644 BritishHorseRacingApi/Models/LastRun.cs create mode 100644 BritishHorseRacingApi/Models/Lineage.cs create mode 100644 BritishHorseRacingApi/Models/LineageAnimal.cs create mode 100644 BritishHorseRacingApi/Models/NonRunner.cs create mode 100644 BritishHorseRacingApi/Models/Official.cs create mode 100644 BritishHorseRacingApi/Models/PagedResult.cs create mode 100644 BritishHorseRacingApi/Models/PerformanceDetail.cs create mode 100644 BritishHorseRacingApi/Models/Race.cs create mode 100644 BritishHorseRacingApi/Models/RacePlanningCode.cs create mode 100644 BritishHorseRacingApi/Models/Racecourse.cs create mode 100644 BritishHorseRacingApi/Models/RacingTrackType.cs create mode 100644 BritishHorseRacingApi/Models/RecentPerformance.cs create mode 100644 BritishHorseRacingApi/Models/Region.cs create mode 100644 BritishHorseRacingApi/Models/Runner.cs create mode 100644 BritishHorseRacingApi/Models/RunnerResult.cs create mode 100644 BritishHorseRacingApi/Models/SexType.cs create mode 100644 BritishHorseRacingApi/Models/SingleResult.cs create mode 100644 BritishHorseRacingApi/Models/Status.cs create mode 100644 BritishHorseRacingApi/Models/TokenResponse.cs create mode 100644 BritishHorseRacingApi/Models/TrackStyle.cs create mode 100644 BritishHorseRacingApi/Models/WinnersDetail.cs create mode 100644 Jenkinsfile diff --git a/BritishHorseRacingApi.Tests/ApiClientTests.cs b/BritishHorseRacingApi.Tests/ApiClientTests.cs new file mode 100644 index 0000000..dd68271 --- /dev/null +++ b/BritishHorseRacingApi.Tests/ApiClientTests.cs @@ -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); + + } + } +} diff --git a/BritishHorseRacingApi.Tests/BritishHorseRacingApi.Tests.csproj b/BritishHorseRacingApi.Tests/BritishHorseRacingApi.Tests.csproj new file mode 100644 index 0000000..3501b94 --- /dev/null +++ b/BritishHorseRacingApi.Tests/BritishHorseRacingApi.Tests.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/BritishHorseRacingApi.Tests/DataConverterHelperTests.cs b/BritishHorseRacingApi.Tests/DataConverterHelperTests.cs new file mode 100644 index 0000000..3f4919b --- /dev/null +++ b/BritishHorseRacingApi.Tests/DataConverterHelperTests.cs @@ -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); + } + } +} diff --git a/BritishHorseRacingApi.sln b/BritishHorseRacingApi.sln new file mode 100644 index 0000000..992b6d2 --- /dev/null +++ b/BritishHorseRacingApi.sln @@ -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 diff --git a/BritishHorseRacingApi/ApiClient.cs b/BritishHorseRacingApi/ApiClient.cs new file mode 100644 index 0000000..8b0e804 --- /dev/null +++ b/BritishHorseRacingApi/ApiClient.cs @@ -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 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(); + 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> GetAllRacesForDay(int year, int month, int day) + { + var fixtures = await GetAllFixturesForDay(year, month, day); + var races = new List(); + foreach(var fixture in fixtures) + { + var page = await GetRaces(year, fixture.FixtureId); + races.AddRange(page.Data); + } + + return races; + } + + public async Task> GetAllRaceResultsForDay(int year, int month, int day) + { + var races = await GetAllRacesForDay(year, month, day); + var runnerResults = new List(); + foreach (var race in races) + { + var page = await GetRaceResults(year, race.RaceId, race.DivisionSequence); + runnerResults.AddRange(page.Data); + } + return runnerResults; + } + + public async Task> 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(uriBuilder.ToString()); + return results; + } + + public async Task GetJockey(int id) + { + var result = await ExecuteRequest>(Endpoints.Jockeys + $"/{id}", (token) => { + return !(token["data"] is JArray); + }); + + if(result == null) + { + return null; + } + + return result.Data; + } + + public async Task> GetRaceResults(int year, int raceId, int divisionSequence = 0) + { + var requestUrl = String.Format(Endpoints.RaceResults, year, raceId, divisionSequence); + return await ExecuteRequestForPagedResults(requestUrl); + } + + public async Task> GetRaceRunners(int year, int raceId, int divisionSequence = 0) + { + var requestUrl = String.Format(Endpoints.Runners, year, raceId, divisionSequence); + return await ExecuteRequestForPagedResults(requestUrl); + } + + public async Task> GetRacecourses() + { + return await ExecuteRequestForPagedResults(Endpoints.Racecourses); + } + + public async Task GetRacecourse(int id) + { + var result = await ExecuteRequest>(Endpoints.Racecourses + "/" + id); + return result.Data; + } + + public async Task> GetRaces(int year, int fixtureId) + { + var finalUrl = String.Format(Endpoints.Races, year, fixtureId); + return await ExecuteRequestForPagedResults(finalUrl); + } + + public async Task GetAnimal(int id) + { + var endpoint = String.Format(Endpoints.Animals, id); + var result = await ExecuteRequest>(endpoint, (token) => !(token["data"] is JArray)); + + if (result == null) + { + return null; + } + + return result.Data; + } + + public async Task> GetAllFixturesForDay(int year, int month, int day) + { + var fixtures = new List(); + 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> 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(finalUrl); + } + + private Task> ExecuteRequestForPagedResults(string endpoint) + { + return ExecuteRequest>(endpoint); + } + + private async Task ExecuteRequest(string endpoint, Func 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(); + 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"); + } + } +} diff --git a/BritishHorseRacingApi/BaseApiClient.cs b/BritishHorseRacingApi/BaseApiClient.cs new file mode 100644 index 0000000..f37aae5 --- /dev/null +++ b/BritishHorseRacingApi/BaseApiClient.cs @@ -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 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); + } +} diff --git a/BritishHorseRacingApi/BritishHorseRacingApi.csproj b/BritishHorseRacingApi/BritishHorseRacingApi.csproj new file mode 100644 index 0000000..d2f7546 --- /dev/null +++ b/BritishHorseRacingApi/BritishHorseRacingApi.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/BritishHorseRacingApi/FalseOrObjectConverter.cs b/BritishHorseRacingApi/FalseOrObjectConverter.cs new file mode 100644 index 0000000..7e84b2c --- /dev/null +++ b/BritishHorseRacingApi/FalseOrObjectConverter.cs @@ -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; + } + } +} diff --git a/BritishHorseRacingApi/Helpers/DataConverterHelper.cs b/BritishHorseRacingApi/Helpers/DataConverterHelper.cs new file mode 100644 index 0000000..b8b23a0 --- /dev/null +++ b/BritishHorseRacingApi/Helpers/DataConverterHelper.cs @@ -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(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; + } + } +} diff --git a/BritishHorseRacingApi/Models/Animal.cs b/BritishHorseRacingApi/Models/Animal.cs new file mode 100644 index 0000000..b7dba80 --- /dev/null +++ b/BritishHorseRacingApi/Models/Animal.cs @@ -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; } + } + +} diff --git a/BritishHorseRacingApi/Models/Career.cs b/BritishHorseRacingApi/Models/Career.cs new file mode 100644 index 0000000..1ea6aad --- /dev/null +++ b/BritishHorseRacingApi/Models/Career.cs @@ -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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/Country.cs b/BritishHorseRacingApi/Models/Country.cs new file mode 100644 index 0000000..9bb55cf --- /dev/null +++ b/BritishHorseRacingApi/Models/Country.cs @@ -0,0 +1,5 @@ +namespace BritishHorseRacingApi.Models +{ + public enum Country { Uk }; + +} diff --git a/BritishHorseRacingApi/Models/Fixture.cs b/BritishHorseRacingApi/Models/Fixture.cs new file mode 100644 index 0000000..3ed2dce --- /dev/null +++ b/BritishHorseRacingApi/Models/Fixture.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/FixtureSession.cs b/BritishHorseRacingApi/Models/FixtureSession.cs new file mode 100644 index 0000000..4b223b1 --- /dev/null +++ b/BritishHorseRacingApi/Models/FixtureSession.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum FixtureSession { Afternoon, Evening, Floodlit }; +} diff --git a/BritishHorseRacingApi/Models/FixtureType.cs b/BritishHorseRacingApi/Models/FixtureType.cs new file mode 100644 index 0000000..6ba7bf4 --- /dev/null +++ b/BritishHorseRacingApi/Models/FixtureType.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum FixtureType { Flat, Jump, Mixed }; +} diff --git a/BritishHorseRacingApi/Models/Jockey.cs b/BritishHorseRacingApi/Models/Jockey.cs new file mode 100644 index 0000000..cb6248d --- /dev/null +++ b/BritishHorseRacingApi/Models/Jockey.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/JockeySummary.cs b/BritishHorseRacingApi/Models/JockeySummary.cs new file mode 100644 index 0000000..3555760 --- /dev/null +++ b/BritishHorseRacingApi/Models/JockeySummary.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/LastRun.cs b/BritishHorseRacingApi/Models/LastRun.cs new file mode 100644 index 0000000..8bbbf64 --- /dev/null +++ b/BritishHorseRacingApi/Models/LastRun.cs @@ -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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/Lineage.cs b/BritishHorseRacingApi/Models/Lineage.cs new file mode 100644 index 0000000..032857c --- /dev/null +++ b/BritishHorseRacingApi/Models/Lineage.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/LineageAnimal.cs b/BritishHorseRacingApi/Models/LineageAnimal.cs new file mode 100644 index 0000000..edc4b27 --- /dev/null +++ b/BritishHorseRacingApi/Models/LineageAnimal.cs @@ -0,0 +1,9 @@ +namespace BritishHorseRacingApi.Models +{ + public class LineageAnimal + { + public int AnimalId { get; set; } + public string AnimalName { get; set; } + } + +} diff --git a/BritishHorseRacingApi/Models/NonRunner.cs b/BritishHorseRacingApi/Models/NonRunner.cs new file mode 100644 index 0000000..44ebb7a --- /dev/null +++ b/BritishHorseRacingApi/Models/NonRunner.cs @@ -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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/Official.cs b/BritishHorseRacingApi/Models/Official.cs new file mode 100644 index 0000000..0151649 --- /dev/null +++ b/BritishHorseRacingApi/Models/Official.cs @@ -0,0 +1,8 @@ +namespace BritishHorseRacingApi.Models +{ + public partial class Official + { + public string Category { get; set; } + public string[] Officials { get; set; } + } +} diff --git a/BritishHorseRacingApi/Models/PagedResult.cs b/BritishHorseRacingApi/Models/PagedResult.cs new file mode 100644 index 0000000..37189ef --- /dev/null +++ b/BritishHorseRacingApi/Models/PagedResult.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace BritishHorseRacingApi.Models +{ + public class PagedResult + { + 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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/PerformanceDetail.cs b/BritishHorseRacingApi/Models/PerformanceDetail.cs new file mode 100644 index 0000000..1eb45d2 --- /dev/null +++ b/BritishHorseRacingApi/Models/PerformanceDetail.cs @@ -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; } + } + +} diff --git a/BritishHorseRacingApi/Models/Race.cs b/BritishHorseRacingApi/Models/Race.cs new file mode 100644 index 0000000..6da7c30 --- /dev/null +++ b/BritishHorseRacingApi/Models/Race.cs @@ -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); + } +} diff --git a/BritishHorseRacingApi/Models/RacePlanningCode.cs b/BritishHorseRacingApi/Models/RacePlanningCode.cs new file mode 100644 index 0000000..1f81e64 --- /dev/null +++ b/BritishHorseRacingApi/Models/RacePlanningCode.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum RacePlanningCode { Normal }; +} diff --git a/BritishHorseRacingApi/Models/Racecourse.cs b/BritishHorseRacingApi/Models/Racecourse.cs new file mode 100644 index 0000000..b74ae5a --- /dev/null +++ b/BritishHorseRacingApi/Models/Racecourse.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/RacingTrackType.cs b/BritishHorseRacingApi/Models/RacingTrackType.cs new file mode 100644 index 0000000..8597240 --- /dev/null +++ b/BritishHorseRacingApi/Models/RacingTrackType.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum RacingTrackType { Awt, Turf, Mixed }; +} diff --git a/BritishHorseRacingApi/Models/RecentPerformance.cs b/BritishHorseRacingApi/Models/RecentPerformance.cs new file mode 100644 index 0000000..8edb451 --- /dev/null +++ b/BritishHorseRacingApi/Models/RecentPerformance.cs @@ -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; } + } +} diff --git a/BritishHorseRacingApi/Models/Region.cs b/BritishHorseRacingApi/Models/Region.cs new file mode 100644 index 0000000..bd3d358 --- /dev/null +++ b/BritishHorseRacingApi/Models/Region.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum Region { Midlands, North, South }; +} diff --git a/BritishHorseRacingApi/Models/Runner.cs b/BritishHorseRacingApi/Models/Runner.cs new file mode 100644 index 0000000..104c9c9 --- /dev/null +++ b/BritishHorseRacingApi/Models/Runner.cs @@ -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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/RunnerResult.cs b/BritishHorseRacingApi/Models/RunnerResult.cs new file mode 100644 index 0000000..499fc9b --- /dev/null +++ b/BritishHorseRacingApi/Models/RunnerResult.cs @@ -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(); + } +} diff --git a/BritishHorseRacingApi/Models/SexType.cs b/BritishHorseRacingApi/Models/SexType.cs new file mode 100644 index 0000000..3b1173e --- /dev/null +++ b/BritishHorseRacingApi/Models/SexType.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum SexType { Filly, Gelding }; +} diff --git a/BritishHorseRacingApi/Models/SingleResult.cs b/BritishHorseRacingApi/Models/SingleResult.cs new file mode 100644 index 0000000..a2207fe --- /dev/null +++ b/BritishHorseRacingApi/Models/SingleResult.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace BritishHorseRacingApi.Models +{ + + public class SingleResult + { + public T Data { get; set; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/Status.cs b/BritishHorseRacingApi/Models/Status.cs new file mode 100644 index 0000000..5ef7d8f --- /dev/null +++ b/BritishHorseRacingApi/Models/Status.cs @@ -0,0 +1,4 @@ +namespace BritishHorseRacingApi.Models +{ + public enum Status { Runner, Dnf, Withdrawn }; +} diff --git a/BritishHorseRacingApi/Models/TokenResponse.cs b/BritishHorseRacingApi/Models/TokenResponse.cs new file mode 100644 index 0000000..f2f2341 --- /dev/null +++ b/BritishHorseRacingApi/Models/TokenResponse.cs @@ -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; } + } +} \ No newline at end of file diff --git a/BritishHorseRacingApi/Models/TrackStyle.cs b/BritishHorseRacingApi/Models/TrackStyle.cs new file mode 100644 index 0000000..1a86dc7 --- /dev/null +++ b/BritishHorseRacingApi/Models/TrackStyle.cs @@ -0,0 +1,5 @@ +namespace BritishHorseRacingApi.Models +{ + public enum TrackStyle { Flat, Jump, Mixed }; + +} diff --git a/BritishHorseRacingApi/Models/WinnersDetail.cs b/BritishHorseRacingApi/Models/WinnersDetail.cs new file mode 100644 index 0000000..7cae2fd --- /dev/null +++ b/BritishHorseRacingApi/Models/WinnersDetail.cs @@ -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; } + } + +} diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..bcffd4a --- /dev/null +++ b/Jenkinsfile @@ -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 */ + } + } +} \ No newline at end of file