public async Task <IpAddressDateRangeDetails> GetIpAddressDetails(string ipAddress, DateTime startDate, DateTime endDate)
        {
            List <IpAddressDetails> ipAddressDetailsList = await _ipAddressDetailsApiDao.GetIpAddressDetails(ipAddress, startDate, endDate);

            _ipAddressDateRangeDetailsBuilder.SetIpAddress(ipAddress);

            foreach (IpAddressDetails ipAddressDetails in ipAddressDetailsList.OrderBy(x => x.Date))
            {
                // Add this date's AS Info
                _ipAddressDateRangeDetailsBuilder.AddAsInfo(ipAddressDetails.Date, ipAddressDetails.AsNumber.GetValueOrDefault(0), ipAddressDetails.Description, ipAddressDetails.CountryCode);

                // Add this date's BlockList info
                ipAddressDetails.BlockListOccurrences
                .GroupBy(x => x.Source)
                .Select(sourceGroup => new
                {
                    Source = sourceGroup.Key,
                    Flags  = sourceGroup.Select(x => new Flag(x.Flag, x.Description)).ToList()
                })
                .ToList()
                .ForEach(source => _ipAddressDateRangeDetailsBuilder.AddBlockListDetail(ipAddressDetails.Date, source.Source, source.Flags));

                // Add this date's ReverseDNS info (using forwardMatch if available)
                if (ipAddressDetails.ReverseDnsResponses.Any())
                {
                    List <ReverseDnsResponse> orderedByHost = ipAddressDetails.ReverseDnsResponses
                                                              .OrderBy(x => x.Host)
                                                              .ToList();

                    List <ReverseDnsResponse> matchingHosts = orderedByHost
                                                              .Where(x => x.IpAddresses.Contains(ipAddress))
                                                              .ToList();

                    ReverseDnsResponse bestHost = matchingHosts
                                                  .Concat(orderedByHost)
                                                  .First();

                    _ipAddressDateRangeDetailsBuilder.AddReverseDnsDetail(ipAddressDetails.Date, bestHost.Host, bestHost.OrganisationalDomain, matchingHosts.Any());
                }
            }

            return(_ipAddressDateRangeDetailsBuilder.GetDetails());
        }
        private async Task <List <(DateTime, ReverseDnsResult)> > GetExistingReverseDnsResults(Stopwatch stopwatch)
        {
            Console.WriteLine($"Querying all reverse dns results");

            List <(DateTime, ReverseDnsResult)> results = new List <(DateTime, ReverseDnsResult)>();
            double tally = 0;

            using (DbDataReader reader = await MySqlHelper.ExecuteReaderAsync(Environment.GetEnvironmentVariable("ReverseDnsConnectionString"), "SELECT * FROM reverse_lookup_results"))
            {
                while (await reader.ReadAsync())
                {
                    ReverseDnsResponse data = JsonConvert.DeserializeObject <List <ReverseDnsResponse> >(reader.GetString("data")).FirstOrDefault();

                    List <ReverseDnsResponse> reverseDnsResponses = null;
                    if (data != null)
                    {
                        List <string> ipAddresses          = data.IpAddresses;
                        string        host                 = data.Host;
                        string        organisationalDomain = data.OrganisationalDomain;

                        reverseDnsResponses = new List <ReverseDnsResponse>
                        {
                            new ReverseDnsResponse(host, ipAddresses, organisationalDomain)
                        };
                    }

                    DateTime date = reader.GetDateTime("date");
                    string   originalIpAddress = reader.GetString("ip_address");

                    ReverseDnsResult reverseDnsResult = new ReverseDnsResult(originalIpAddress, reverseDnsResponses);
                    results.Add((date, reverseDnsResult));

                    tally++;
                    double percentDone = tally / 4500000d;
                    Console.Write($"\rLoaded {tally} = {percentDone:P2}% after {stopwatch.Elapsed.Minutes} mins {stopwatch.Elapsed.Seconds} secs");
                }
            }

            Console.WriteLine($"\nFinished querying all results");

            return(results);
        }