using GloucesterAsap.Forms.Extensions;
using GloucesterAsap.Forms.PlatformSpecificInterfaces;
using GloucesterAsap.Forms.Services.Interfaces;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace GloucesterAsap.Forms.Services
public class NhsChoicesService : Interfaces.INhsChoicesService
public int odatacount { get; set; }
public Value[] value { get; set; }
public string tracking { get; set; }
public float searchscore { get; set; }
public string OrganisationID { get; set; }
public string OrganisationName { get; set; }
public string[] OrganisationAliases { get; set; }
public string OrganisationTypeID { get; set; }
public string OrganisationType { get; set; }
public string OrganisationStatus { get; set; }
public bool IsPimsManaged { get; set; }
public string PIMSCode { get; set; }
public string NACSCode { get; set; }
public object LocalAuthorityCode { get; set; }
public string SummaryText { get; set; }
public string URL { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string City { get; set; }
public string County { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
public string OrganisationSubType { get; set; }
public DateTime LastUpdatedDate { get; set; }
public string Postcode { get; set; }
public Geocode Geocode { get; set; }
public string[] CCG { get; set; }
public object[] RelatedIAPTCCGs { get; set; }
public object[] Trusts { get; set; }
public object[] CCGLocalAuthority { get; set; }
public string[] ServicesOffered { get; set; }
public string[] ServicesProvided { get; set; }
public string[] ServiceCodesProvided { get; set; }
public string[] ServicesProvidedConditions { get; set; }
public string[] OrgTypeConditions { get; set; }
public string OpeningTimes { get; set; }
public string Contacts { get; set; }
public object VenueType { get; set; }
public string Metrics { get; set; }
public object GSD { get; set; }
public Openingtimesv2[] OpeningTimesV2 { get; set; }
public float distanceMiles { get; set; }
public string type { get; set; }
public float[] coordinates { get; set; }
public Crs crs { get; set; }
public string type { get; set; }
public Properties properties { get; set; }
public string name { get; set; }
public class Openingtimesv2
public string Weekday { get; set; }
public string Times { get; set; }
public int OffsetOpeningTime { get; set; }
public int OffsetClosingTime { get; set; }
public string OpeningTimeType { get; set; }
public string AdditionalOpeningDate { get; set; }
public bool IsOpen { get; set; }
public class ContactsArray
public string OrganisationContactType { get; set; }
public string OrganisationContactAvailabilityType { get; set; }
public string OrganisationContactMethodType { get; set; }
public string OrganisationContactValue { get; set; }
public class ContactsRoot
public List<ContactsArray> ContactsArray { get; set; }
private readonly IConfigurationReader _configurationReader;
private readonly INetworkConnection _networkConnection;
public NhsChoicesService()
_configurationReader = App.DependencyServiceResolver.Get<IConfigurationReader>(Xamarin.Forms.DependencyFetchTarget.GlobalInstance) ?? throw new Exception($"Failed to resolve {nameof(IConfigurationReader)}");
_networkConnection = App.DependencyServiceResolver.Get<INetworkConnection>() ?? throw new Exception($"Failed to resolve {nameof(INetworkConnection)}");
public async Task<bool> FetchNhsChoicesDataForUserLocationAsync(string postcode, int userLocationId)
if (string.IsNullOrWhiteSpace(postcode))
throw new ArgumentNullException($"{nameof(postcode)}");
if (!await _networkConnection.HasNetworkConnectionAsync())
System.Diagnostics.Debug.WriteLine("Detected no internet connection.");
await this.GetGpPracticesByPostcodeAndStoreInTableAsync(postcode, userLocationId);
await this.GetPharmaciesByPostcodeAndStoreInTableAsync(postcode, userLocationId);
await this.FetchOpeningTimesForAllOrganisationsAsync(userLocationId);
System.Diagnostics.Debug.WriteLine($"Exception throw when trying to download NHS choices data {ex}");
public async Task<bool> DoesNhsChoicesDataExistForLocationAsync(int locationId)
return await App.DatabaseRepository.AnyAsync<DAL.Entities.NhsChoicesData>(x => x.UserLocationId == locationId);
public async Task<bool> DeleteNhsChoicesDataForLocationAsync(int locationId)
if (await this.DoesNhsChoicesDataExistForLocationAsync(locationId))
var matchingLocationById = await App.DatabaseRepository.GetFirstOrDefaultAsync<DAL.Entities.UserLocation>(x => x.Id == locationId);
if (matchingLocationById != null)
var relatedNhsChoicesDataForLocation = await matchingLocationById.GetAllRelatedNhsChoicesDataForThisLocationAsync();
if (!relatedNhsChoicesDataForLocation.IsNullOrEmpty())
return await App.DatabaseRepository.DeleteAsync(relatedNhsChoicesDataForLocation) >= 1;
#region Organisation fetches and inserts
private async Task GetGpPracticesByPostcodeAndStoreInTableAsync(string postcode, long userLocationId)
var path = "https://api.nhs.uk/service-search/search-postcode-or-place?api-version=1&search=" + postcode;
var subscriptionKey = "cdadee486e564c2dbe2d85b8c29bc7fb";
var gpFilterQuery = "OrganisationTypeID eq 'GPB'";
var queryBodyContent = JsonConvert.SerializeObject(
var data = new StringContent(queryBodyContent, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
client.DefaultRequestHeaders.Add("subscription-key", subscriptionKey);
var response = client.PostAsync(path, data).Result.Content.ReadAsStringAsync().Result;
dynamic deserialisedObj = JsonConvert.DeserializeObject(response);
var result = deserialisedObj;
Rootobject rootobject = JsonConvert.DeserializeObject<Rootobject>(response);
foreach (var item in rootobject.value)
var orgId = item.OrganisationID;
var nacs = item.NACSCode;
var orgName = (!item.OrganisationAliases.IsNullOrEmpty() && item.OrganisationAliases.Any()) ? item.OrganisationAliases.First() : item.OrganisationName;
var odsCode = item.OrganisationID;
var addressLine1 = item.Address1;
var addressLine2 = item.Address2;
var addressLine3 = item.Address3;
var orgPostcode = item.Postcode;
var distanceInMiles = item.distanceMiles;
var orgLong = item.Longitude;
var orgLat = item.Latitude;
if (!string.IsNullOrEmpty(item.Contacts))
var contacts = JsonConvert.DeserializeObject<List<ContactsArray>>(item.Contacts);
foreach (var contact in contacts)
var contactMethodType = contact.OrganisationContactMethodType;
var contactValue = contact.OrganisationContactValue;
if (contactMethodType == "Telephone" && !string.IsNullOrWhiteSpace(contactValue))
telephone = contactValue;
var entityToSave = new DAL.Entities.NhsChoicesData(orgId, orgName,
addressLine1, addressLine2, addressLine3, null,
orgPostcode, distanceInMiles, telephone, Enums.ServiceType.Gp, orgLat, orgLong, userLocationId);
var numberOfSavedRecords = await App.DatabaseRepository.SaveAsync(entityToSave);
System.Diagnostics.Debug.WriteLine($"Saved {numberOfSavedRecords} GP practice records.");
private async Task GetPharmaciesByPostcodeAndStoreInTableAsync(string postcode, long userLocationId)
var path = "https://api.nhs.uk/service-search/search-postcode-or-place?api-version=1&search=" + postcode;
var subscriptionKey = "cdadee486e564c2dbe2d85b8c29bc7fb";
var pharmaFilterQuery = "OrganisationTypeID eq 'PHA'";
var queryBodyContent = JsonConvert.SerializeObject(
filter = pharmaFilterQuery,
var data = new StringContent(queryBodyContent, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
client.DefaultRequestHeaders.Add("subscription-key", subscriptionKey);
var response = client.PostAsync(path, data).Result.Content.ReadAsStringAsync().Result;
dynamic deserialisedObj = JsonConvert.DeserializeObject(response);
var result = deserialisedObj;
Rootobject rootobject = JsonConvert.DeserializeObject<Rootobject>(response);
foreach (var item in rootobject.value)
var orgId = item.OrganisationID;
var nacs = item.NACSCode;
var orgName = (!item.OrganisationAliases.IsNullOrEmpty() && item.OrganisationAliases.Any()) ? item.OrganisationAliases.First() : item.OrganisationName;
var odsCode = item.OrganisationID;
var addressLine1 = item.Address1;
var addressLine2 = item.Address2;
var addressLine3 = item.Address3;
var orgPostcode = item.Postcode;
var distanceInMiles = item.distanceMiles;
var orgLong = item.Longitude;
var orgLat = item.Latitude;
if (!string.IsNullOrEmpty(item.Contacts))
var contacts = JsonConvert.DeserializeObject<List<ContactsArray>>(item.Contacts);
foreach (var contact in contacts)
var contactMethodType = contact.OrganisationContactMethodType;
var contactValue = contact.OrganisationContactValue;
if (contactMethodType == "Telephone" && !string.IsNullOrWhiteSpace(contactValue))
telephone = contactValue;
var entityToSave = new DAL.Entities.NhsChoicesData(orgId, orgName,
addressLine1, addressLine2, addressLine3, null,
orgPostcode, distanceInMiles, telephone, Enums.ServiceType.Pharmacy, orgLat, orgLong, userLocationId);
var numberOfSavedRecords = await App.DatabaseRepository.SaveAsync(entityToSave);
System.Diagnostics.Debug.WriteLine($"Saved {numberOfSavedRecords} {nameof(Enums.ServiceType.Pharmacy)} records.");
private async Task GetHospitalsByPostcodeAndStoreInTableAsync(string postcode, long userLocationId)
var fullyQualifiedUrl = $"{this.BaseNhsChoicesUrl}/organisations/hospitals/postcode/{postcode}.xml?apikey={this.NhsChoicesApiKey}&range=100";
var xmlDataForGpPractices = await Helpers.WebRequestHelpers.LoadXmlStringFromUrlAsync(fullyQualifiedUrl);
if (string.IsNullOrWhiteSpace(xmlDataForGpPractices))
throw new Exception($"returned value for {nameof(xmlDataForGpPractices)} was null or whitespace.");
var deserializedXml = Helpers.SerializeHelper.DeserializeXmlToObject<Models.NhsChoicesModel.HospitalModel>(xmlDataForGpPractices);
if (deserializedXml == null || deserializedXml.EntryLinks.IsNullOrEmpty())
throw new Exception($"Failed to deserialize the XML to an object type of {nameof(Models.NhsChoicesModel.HospitalModel)}");
foreach (var item in deserializedXml.EntryLinks)
var content = item.Content;
var organisationSummary = content.OrganisationSummary;
var addressInformation = organisationSummary.address;
var geographyInformation = organisationSummary.GeographicCoordinates;
var telephoneNumber = organisationSummary?.SummaryContactInformation?.TelephoneNumber ?? string.Empty;
var addressLine1 = this.GetAddressLineForIndex(addressInformation?.addressLine, 0);
var addressLine2 = this.GetAddressLineForIndex(addressInformation?.addressLine, 1);
var addressLine3 = this.GetAddressLineForIndex(addressInformation?.addressLine, 2);
var addressLine4 = this.GetAddressLineForIndex(addressInformation?.addressLine, 3);
var entityToSave = new DAL.Entities.NhsChoicesData(item.Id, item.Title,
organisationSummary.ODSCode,
addressLine1, addressLine2, addressLine3, addressLine4,
addressInformation.postcode, organisationSummary.Distance, telephoneNumber, Enums.ServiceType.Hospital, geographyInformation.Latitude, geographyInformation.Longitude, userLocationId);
var numberOfSavedRecords = await App.DatabaseRepository.SaveAsync(entityToSave);
System.Diagnostics.Debug.WriteLine($"Saved {numberOfSavedRecords} {nameof(Enums.ServiceType.Hospital)} records.");
#region Opening times fetch
private async Task FetchOpeningTimesForAllOrganisationsAsync(int userLocationId)
var allOrganisationEntitiesFromLocalDbForLocationId = await App.DatabaseRepository.GetAllAsync<DAL.Entities.NhsChoicesData>(x => x.UserLocationId == userLocationId);
foreach (var entity in allOrganisationEntitiesFromLocalDbForLocationId)
if (string.IsNullOrWhiteSpace(entity.ChoicesUrl))
System.Diagnostics.Debug.WriteLine($"Error! NhsChoicesData entity did not have a valid value {nameof(entity.ChoicesUrl)} for id --> {entity.Id}. Skipping to next record.");
if (entity.ServiceType == Enums.ServiceType.Hospital)
var nacs = entity.OdsCode;
var path = "https://api.nhs.uk/service-search/search?api-version=1";
var subscriptionKey = "cdadee486e564c2dbe2d85b8c29bc7fb";
var filterQuery = "NACSCode eq '" + nacs + "'";
var queryBodyContent = JsonConvert.SerializeObject(
select = "OpeningTimesV2",
var data = new StringContent(queryBodyContent, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
client.DefaultRequestHeaders.Add("subscription-key", subscriptionKey);
var response = client.PostAsync(path, data).Result.Content.ReadAsStringAsync().Result;
dynamic deserialisedObj = JsonConvert.DeserializeObject(response);
var result = deserialisedObj;
Rootobject rootobject = JsonConvert.DeserializeObject<Rootobject>(response);
if (!rootobject.value.IsNullOrEmpty())
foreach (var item in rootobject.value)
if (!item.OpeningTimesV2.IsNullOrEmpty() && item.OpeningTimesV2.Any())
if (entity.ServiceType == Enums.ServiceType.Gp)
var receptionTimes = item.OpeningTimesV2.Where(x => x.OpeningTimeType == "Reception");
var rectest = receptionTimes.Any();
var mondayTimes = receptionTimes.Where(x => x.Weekday == "Monday");
var tuesdayTimes = receptionTimes.Where(x => x.Weekday == "Tuesday");
var wednesdayTimes = receptionTimes.Where(x => x.Weekday == "Wednesday");
var thursdayTimes = receptionTimes.Where(x => x.Weekday == "Thursday");
var fridayTimes = receptionTimes.Where(x => x.Weekday == "Friday");
var saturdayTimes = receptionTimes.Where(x => x.Weekday == "Saturday");
var sundayTimes = receptionTimes.Where(x => x.Weekday == "Sunday");
string mondayOpeningTimes = null; string tuesdayOpeningTimes = null; string wednesdayOpeningTimes = null; string thursdayOpeningTimes = null; string fridayOpeningTimes = null; string saturdayOpeningTimes = null; string sundayOpeningTimes = null;
if (mondayTimes.Any() && mondayTimes.Count() > 1)
foreach (var time in mondayTimes)
mondayOpeningTimes += time.Times;
else if (mondayTimes.Any()) { mondayOpeningTimes = mondayTimes?.FirstOrDefault().Times; }
if (tuesdayTimes.Any() && tuesdayTimes.Count() > 1)
foreach (var time in tuesdayTimes)
tuesdayOpeningTimes += time.Times;
else if (tuesdayTimes.Any()) { tuesdayOpeningTimes = tuesdayTimes?.FirstOrDefault().Times; }
if (wednesdayTimes.Any() && wednesdayTimes.Count() > 1)
foreach (var time in wednesdayTimes)
wednesdayOpeningTimes += time.Times;
else if (wednesdayTimes.Any()) { wednesdayOpeningTimes = wednesdayTimes?.FirstOrDefault().Times; }
if (thursdayTimes.Any() && thursdayTimes.Count() > 1)
foreach (var time in thursdayTimes)
thursdayOpeningTimes += time.Times;
else if (thursdayTimes.Any()) { thursdayOpeningTimes = thursdayTimes?.FirstOrDefault().Times; }
if (fridayTimes.Any() && fridayTimes.Count() > 1)
foreach (var time in fridayTimes)
fridayOpeningTimes += time.Times;
else if (fridayTimes.Any()) { fridayOpeningTimes = fridayTimes?.FirstOrDefault().Times; }
if (saturdayTimes.Any() && saturdayTimes.Count() > 1)
foreach (var time in saturdayTimes)
saturdayOpeningTimes += time.Times;
else if (saturdayTimes.Any()) { saturdayOpeningTimes = saturdayTimes?.FirstOrDefault().Times; }
if (sundayTimes.Any() && sundayTimes.Count() > 1)
foreach (var time in sundayTimes)
sundayOpeningTimes += time.Times;
else if (sundayTimes.Any()) { sundayOpeningTimes = sundayTimes?.FirstOrDefault().Times; }
entity.UpdateOpeniningTimes(mondayOpeningTimes, tuesdayOpeningTimes, wednesdayOpeningTimes, thursdayOpeningTimes, fridayOpeningTimes, saturdayOpeningTimes, sundayOpeningTimes);
var didSucessfullyUpdatEntity = await App.DatabaseRepository.UpdateAsync(entity) >= 1;
if (didSucessfullyUpdatEntity)
System.Diagnostics.Debug.WriteLine($"Sucessfully updated entity id {entity.Id} with reception opening times.");
System.Diagnostics.Debug.WriteLine($"ERROR! FAILED TO UPDATE entity id {entity.Id} with reception opening times.");
else if (entity.ServiceType == Enums.ServiceType.Pharmacy)
var generalTimes = item.OpeningTimesV2.Where(x => x.OpeningTimeType == "General");
var gentest = generalTimes.Any();
var mondayTimes = generalTimes.Where(x => x.Weekday == "Monday");
var tuesdayTimes = generalTimes.Where(x => x.Weekday == "Tuesday");
var wednesdayTimes = generalTimes.Where(x => x.Weekday == "Wednesday");
var thursdayTimes = generalTimes.Where(x => x.Weekday == "Thursday");
var fridayTimes = generalTimes.Where(x => x.Weekday == "Friday");
var saturdayTimes = generalTimes.Where(x => x.Weekday == "Saturday");
var sundayTimes = generalTimes.Where(x => x.Weekday == "Sunday");
string mondayOpeningTimes = null; string tuesdayOpeningTimes = null; string wednesdayOpeningTimes = null; string thursdayOpeningTimes = null; string fridayOpeningTimes = null; string saturdayOpeningTimes = null; string sundayOpeningTimes = null;
if (mondayTimes.Any() && mondayTimes.Count() > 1)
foreach (var time in mondayTimes)
mondayOpeningTimes += time.Times;
else if (mondayTimes.Any()) { mondayOpeningTimes = mondayTimes?.FirstOrDefault().Times; }
if (tuesdayTimes.Any() && tuesdayTimes.Count() > 1)
foreach (var time in tuesdayTimes)
tuesdayOpeningTimes += time.Times;
else if (tuesdayTimes.Any()) { tuesdayOpeningTimes = tuesdayTimes?.FirstOrDefault().Times; }
if (wednesdayTimes.Any() && wednesdayTimes.Count() > 1)
foreach (var time in wednesdayTimes)
wednesdayOpeningTimes += time.Times;
else if (wednesdayTimes.Any()) { wednesdayOpeningTimes = wednesdayTimes?.FirstOrDefault().Times; }
if (thursdayTimes.Any() && thursdayTimes.Count() > 1)
foreach (var time in thursdayTimes)
thursdayOpeningTimes += time.Times;
else if (thursdayTimes.Any()) { thursdayOpeningTimes = thursdayTimes?.FirstOrDefault().Times; }
if (fridayTimes.Any() && fridayTimes.Count() > 1)
foreach (var time in fridayTimes)
fridayOpeningTimes += time.Times;
else if (fridayTimes.Any()) { fridayOpeningTimes = fridayTimes?.FirstOrDefault().Times; }
if (saturdayTimes.Any() && saturdayTimes.Count() > 1)
foreach (var time in saturdayTimes)
saturdayOpeningTimes += time.Times;
else if (saturdayTimes.Any()) { saturdayOpeningTimes = saturdayTimes?.FirstOrDefault().Times; }
if (sundayTimes.Any() && sundayTimes.Count() > 1)
foreach (var time in sundayTimes)
sundayOpeningTimes += time.Times;
else if (sundayTimes.Any()) { sundayOpeningTimes = sundayTimes?.FirstOrDefault().Times; }
entity.UpdateOpeniningTimes(mondayOpeningTimes, tuesdayOpeningTimes, wednesdayOpeningTimes, thursdayOpeningTimes, fridayOpeningTimes, saturdayOpeningTimes, sundayOpeningTimes);
var didSucessfullyUpdatEntity = await App.DatabaseRepository.UpdateAsync(entity) >= 1;
if (didSucessfullyUpdatEntity)
System.Diagnostics.Debug.WriteLine($"Sucessfully updated entity id {entity.Id} with opening times.");
System.Diagnostics.Debug.WriteLine($"ERROR! FAILED TO UPDATE entity id {entity.Id} with opening times.");
System.Diagnostics.Debug.WriteLine($"Error >> {ex}");
System.Diagnostics.Debug.WriteLine($"Error >> {ex}");
private string ExtractOpeningTimeForDay(Models.NhsChoicesModel.openingTimesTimesSessionTypeDayOfWeek dayOfWeekModel)
if (dayOfWeekModel != null)
if (dayOfWeekModel != null
&& !dayOfWeekModel.timesSessions.IsNullOrEmpty())
var openingTimes = new List<string>();
foreach (var timeSession in dayOfWeekModel.timesSessions)
if (!string.IsNullOrWhiteSpace(timeSession.fromTime)
&& !string.IsNullOrWhiteSpace(timeSession.toTime))
openingTimes.Add($"{timeSession.fromTime} - {timeSession.toTime}");
else if (!string.IsNullOrWhiteSpace(timeSession.fromTime)
&& string.IsNullOrWhiteSpace(timeSession.toTime))
openingTimes.Add($"{timeSession.fromTime}");
else if (string.IsNullOrWhiteSpace(timeSession.fromTime)
&& !string.IsNullOrWhiteSpace(timeSession.toTime))
openingTimes.Add($"{timeSession.toTime}");
else if (!timeSession.Text.IsNullOrEmpty())
openingTimes.Add($"{timeSession.Text?.FirstOrDefault()}");
return (!openingTimes.IsNullOrEmpty()) ? string.Join("\n", openingTimes) : string.Empty;
return dayOfWeekModel?.timesSessions?.FirstOrDefault()?.Text?.FirstOrDefault() ??
#region Static data/ organisations
private string BaseNhsChoicesUrl => _configurationReader.GetConfigValueFromFile("NhsChoicesApiUrl");
private string NhsChoicesApiKey => _configurationReader.GetConfigValueFromFile("NhsChoicesApiKey");
private string GetAddressLineForIndex(string[] addresses, int index)
if (index < addresses.Length)