using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; using System.Web; using System.Xml; class ElectionDayResults { public XmlDocument ParishVotes { get; set; } public XmlDocument PrecinctVotes { get; set; } } static class ElectionResultsReader { private const string BASE_API_ADDRESS = "https://voterportal.sos.la.gov/api/MediaRequests/"; private const string FORMATTED_ELECTION_DATE = "2017-03-25"; private const string ACCESS_KEY = "<Your Access Key Here>"; private const string SECRET_KEY = "<Your Secret Key Here>"; private static readonly HttpClient httpClient; private static DateTime? latestSavedVersion; static ElectionResultsReader() { httpClient = new HttpClient { BaseAddress = new Uri(BASE_API_ADDRESS), }; } /// <summary> /// Returns an XmlDocument with a list of races and candidates for the election. /// </summary> /// <remarks>This data does not change often and should be requested infrequently.</remarks> public static async Task<XmlDocument> ReadRacesAndCandidates() { return await GetXmlFile("RacesAndCandidates"); } /// <summary> /// If new election day results are available, returns them as a pair of XML documents: /// ParishVotes and PrecinctVotes. If new results are not available, returns null. /// </summary> public static async Task<ElectionDayResults> ReadElectionDayResults(bool includePrecinctLevelVotes) { ElectionDayResults results = null; // Determine if a new version of the election results is available. DateTime? currentResultsVersion = await GetCurrentResultsVersion(); bool isNewVersion = currentResultsVersion.HasValue && (latestSavedVersion == null || currentResultsVersion.Value > latestSavedVersion.Value); // Only request election results when there is a new version. This avoids unnecessary API requests. if (isNewVersion) { latestSavedVersion = currentResultsVersion; // Request Election Results XmlDocument parishVotesXml = await GetXmlFile("ParishVotes"); XmlDocument precinctVotesXml = includePrecinctLevelVotes ? await GetXmlFile("PrecinctVotes") : null; results = new ElectionDayResults { ParishVotes = parishVotesXml, PrecinctVotes = precinctVotesXml }; } return results; } private static async Task<XmlDocument> GetXmlFile(string fileName) { // Request XML File string requestUrl = string.Format("{0}/{1}/{2}/{3}", fileName, FORMATTED_ELECTION_DATE, ACCESS_KEY, SECRET_KEY); HttpResponseMessage response = await httpClient.GetAsync(requestUrl); // If the request was not successful, throw an exception detailing why. if (!response.IsSuccessStatusCode) { string responseContent = response.Content.ReadAsStringAsync().Result; string errorMessage = string.Format("{0} ({1}): {2}", response.StatusCode, (int)response.StatusCode, responseContent); throw new HttpException((int)response.StatusCode, errorMessage); } // Extract XML from HttpResponse Stream xmlStream = await response.Content.ReadAsStreamAsync(); XmlDocument xmlFile = new XmlDocument(); xmlFile.Load(xmlStream); return xmlFile; } private static async Task<DateTime?> GetCurrentResultsVersion() { DateTime? currentResultsVersion = null; // Retrieve XML File Containing Version Information XmlDocument latestVersionXml = await GetXmlFile("LatestVersion"); // Retrieve datetime string from version XML. string dateString = latestVersionXml["Version"] != null ? latestVersionXml["Version"].InnerText : null; // Attempt to parse a date from the XML contents. if (!string.IsNullOrWhiteSpace(dateString)) { DateTime version; if (DateTime.TryParse(dateString, out version)) currentResultsVersion = version; } return currentResultsVersion; } }