Imports System.IO Imports System.Net.Http Imports System.Web Imports System.Xml Class ElectionDayResults Public Property ParishVotes As XmlDocument Public Property PrecinctVotes As XmlDocument End Class Class ElectionResultsReader Private Const BASE_API_ADDRESS As String = "https://voterportal.sos.la.gov/api/MediaRequests/" Private Const FORMATTED_ELECTION_DATE As String = "2017-03-25" Private Const ACCESS_KEY As String = "<Your Access Key Here>" Private Const SECRET_KEY As String = "<Your Secret Key Here>" Private Shared ReadOnly httpClient As HttpClient Private Shared latestSavedVersion As Date? Shared Sub New() httpClient = New HttpClient() With { .BaseAddress = New Uri(BASE_API_ADDRESS) } End Sub ''' <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 Shared Async Function ReadRacesAndCandidates() As Task(Of XmlDocument) Return Await GetXmlFile("RacesAndCandidates") End Function ''' <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 Shared Async Function ReadElectionDayResults(includePrecinctLevelVotes As Boolean) As Task(Of ElectionDayResults) Dim results As ElectionDayResults = Nothing ' Determine if a new version of the election results is available. Dim currentResultsVersion As Date? = Await GetCurrentResultsVersion() Dim isNewVersion As Boolean = currentResultsVersion.HasValue AndAlso (latestSavedVersion Is Nothing OrElse currentResultsVersion.Value > latestSavedVersion.Value) ' Only request election results when there is a new version. This avoids unnecessary API requests. If isNewVersion Then latestSavedVersion = currentResultsVersion ' Request Election Results Dim parishVotesXml As XmlDocument = Await GetXmlFile("ParishVotes") Dim precinctVotesXml As XmlDocument = If(includePrecinctLevelVotes, Await GetXmlFile("PrecinctVotes"), Nothing) ' Bundle Election Results results = New ElectionDayResults() With { .ParishVotes = parishVotesXml, .PrecinctVotes = precinctVotesXml } End If Return results End Function Private Shared Async Function GetXmlFile(fileName As String) As Task(Of XmlDocument) ' Request XML File Dim requestUrl = String.Format("{0}/{1}/{2}/{3}", fileName, FORMATTED_ELECTION_DATE, ACCESS_KEY, SECRET_KEY) Dim response As HttpResponseMessage = Await httpClient.GetAsync(requestUrl) ' If the request was not successful, throw an exception detailing why. If Not response.IsSuccessStatusCode Then Dim responseContents = response.Content.ReadAsStringAsync().Result Dim errorMessage As String = String.Format("{0} ({1}): {2}", response.StatusCode, CInt(response.StatusCode), responseContents) Throw New HttpException(response.StatusCode, errorMessage) End If ' Extract XML from HttpResponse Dim xmlStream As Stream = Await response.Content.ReadAsStreamAsync() Dim xmlFile As New XmlDocument() xmlFile.Load(xmlStream) Return xmlFile End Function Private Shared Async Function GetCurrentResultsVersion() As Task(Of Date?) Dim currentResultsVersion As Date? = Nothing ' Retrieve XML File Containing Version Information Dim latestVersionXml As XmlDocument = Await GetXmlFile("LatestVersion") ' Retrieve datetime string from version XML. Dim dateString As String = If(latestVersionXml("Version") IsNot Nothing, latestVersionXml("Version").InnerText, Nothing) ' Attempt to parse a date from the XML contents. If Not String.IsNullOrWhiteSpace(dateString) Then Dim version As Date If Date.TryParse(dateString, version) Then currentResultsVersion = version End If End If Return currentResultsVersion End Function End Class