public async Task <PostcodeDto> GetPostcodeAsync(string postcode, CancellationToken cancellationToken)
        {
            IEnumerable <PostcodeDto> postcodes = await GetPostcodesAsync(new List <string>() { postcode }, cancellationToken);

            PostcodeDto postcodeDto = postcodes.FirstOrDefault();

            return(postcodeDto);
        }
Exemplo n.º 2
0
        public async Task <GetPostcodeResponse> Handle(GetPostcodeRequest request, CancellationToken cancellationToken)
        {
            request.Postcode = PostcodeFormatter.FormatPostcode(request.Postcode);

            PostcodeDto postcodeDto = await _postcodeAndAddressGetter.GetPostcodeAsync(request.Postcode, cancellationToken);

            GetPostcodeResponse getNearbyGetPostcodesResponse = _mapper.Map <PostcodeDto, GetPostcodeResponse>(postcodeDto);

            getNearbyGetPostcodesResponse.AddressDetails = _addressDetailsSorter.OrderAddressDetailsResponse(getNearbyGetPostcodesResponse.AddressDetails);
            return(getNearbyGetPostcodesResponse);
        }
        public async Task GetPostcodeAsync()
        {
            CancellationToken        cancellationToken        = new CancellationToken();
            PostcodeAndAddressGetter postcodeAndAddressGetter = new PostcodeAndAddressGetter(_repository.Object, _qasAddressGetter.Object, _postcodesWithoutAddressesCache.Object);

            PostcodeDto result = await postcodeAndAddressGetter.GetPostcodeAsync("NG11AA", cancellationToken);

            _repository.Verify(x => x.GetPostcodesAsync(It.Is <IEnumerable <string> >(y => y.Count() == 1 && y.Contains("NG1 1AA"))), Times.Once);

            Assert.AreEqual(1, result.AddressDetails.Count());
            Assert.AreEqual("1_addressline1", result.AddressDetails.FirstOrDefault().AddressLine1);
            Assert.AreEqual("1_addressline2", result.AddressDetails.FirstOrDefault().AddressLine2);
            Assert.AreEqual("1_addressline1", result.AddressDetails.FirstOrDefault().AddressLine3);
            Assert.AreEqual("1_locality", result.AddressDetails.FirstOrDefault().Locality);
        }
Exemplo n.º 4
0
        public PostcodeDto MapToPostcodeDto(string postcode, IEnumerable <QasFormatRootResponse> qasFormatRootResponses)
        {
            DateTime timeNow = DateTime.UtcNow;

            postcode = PostcodeFormatter.FormatPostcode(postcode);
            PostcodeDto postcodeDto = new PostcodeDto();

            postcodeDto.Postcode    = postcode;
            postcodeDto.LastUpdated = timeNow;

            foreach (var qasFormatRootResponse in qasFormatRootResponses)
            {
                AddressDetailsDto addressDetailsDto = new AddressDetailsDto();

                foreach (QasFormatAddressReponse address in qasFormatRootResponse.Address) // to deal with strange way results are returned ...
                {
                    if (!String.IsNullOrWhiteSpace(address.AddressLine1))
                    {
                        addressDetailsDto.AddressLine1 = address.AddressLine1;
                    }
                    else if (!String.IsNullOrWhiteSpace(address.AddressLine2))
                    {
                        addressDetailsDto.AddressLine2 = address.AddressLine2;
                    }
                    else if (!String.IsNullOrWhiteSpace(address.AddressLine3))
                    {
                        addressDetailsDto.AddressLine3 = address.AddressLine3;
                    }
                    else if (!String.IsNullOrWhiteSpace(address.Locality))
                    {
                        addressDetailsDto.Locality = address.Locality;
                    }
                    else if (!String.IsNullOrWhiteSpace(address.PostalCode))
                    {
                        addressDetailsDto.Postcode = PostcodeFormatter.FormatPostcode(address.PostalCode);
                    }
                }
                // filter out postcodes that weren't returned or don't have the expected postcode
                if (!String.IsNullOrWhiteSpace(addressDetailsDto.Postcode) && addressDetailsDto.Postcode == postcode)
                {
                    addressDetailsDto.LastUpdated = timeNow;
                    postcodeDto.AddressDetails.Add(addressDetailsDto);
                }
            }

            return(postcodeDto);
        }
Exemplo n.º 5
0
        public void SetUp()
        {
            _repository = new Mock <IRepository>();



            _repository.SetupAllProperties();


            _qasService = new Mock <IQasService>();

            _qasService.SetupAllProperties();

            _qasMapper = new Mock <IQasMapper>();

            ILookup <string, string> missingQasFormatIdsGroupedByPostCode = new List <KeyValuePair <string, string> >()
            {
                new KeyValuePair <string, string>("ng15ba", "AC4705C7-E358-4338-A4FC-DE5EA241B5F1")
            }.ToLookup(x => x.Key, x => x.Value);

            _qasMapper.Setup(x => x.GetFormatIds(It.IsAny <IEnumerable <QasSearchRootResponse> >())).Returns(missingQasFormatIdsGroupedByPostCode);

            _missingPostcodeDtosFromQas = new PostcodeDto()
            {
                Postcode       = "NG1 6DQ",
                AddressDetails = new List <AddressDetailsDto>()
                {
                    new AddressDetailsDto()
                    {
                        AddressLine1 = "2_addressline1",
                        AddressLine2 = "2_addressline2",
                        AddressLine3 = "2_addressline1",
                        Locality     = "2_locality"
                    }
                }
            };

            _qasMapper.Setup(x => x.MapToPostcodeDto(It.IsAny <string>(), It.IsAny <IEnumerable <QasFormatRootResponse> >())).Returns(_missingPostcodeDtosFromQas);

            _friendlyNameGenerator = new Mock <IFriendlyNameGenerator>();
            _friendlyNameGenerator.Setup(x => x.GenerateFriendlyName(It.IsAny <PostcodeDto>()));

            _logger = new Mock <ILoggerWrapper <QasAddressGetter> >();

            _logger.SetupAllProperties();
        }
        public void MapToPostcodeDto()
        {
            QasMapper qasMapper = new QasMapper();

            var postCode         = "ng1 5fs";
            var expectedPostcode = PostcodeFormatter.FormatPostcode(postCode);

            IEnumerable <QasFormatRootResponse> qasFormatRootResponses = new List <QasFormatRootResponse>()
            {
                new QasFormatRootResponse()
                {
                    Address = new List <QasFormatAddressReponse>()
                    {
                        new QasFormatAddressReponse()
                        {
                            PostalCode = postCode
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine1 = "line1"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine2 = "line2"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine3 = "line3"
                        },
                        new QasFormatAddressReponse()
                        {
                            Locality = "loc"
                        },
                    }
                },
                new QasFormatRootResponse()
                {
                    Address = new List <QasFormatAddressReponse>()
                    {
                        new QasFormatAddressReponse()
                        {
                            PostalCode = "FilterMeOut"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine1 = "line1"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine2 = "line2"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine3 = "line3"
                        },
                        new QasFormatAddressReponse()
                        {
                            Locality = "loc"
                        }
                    }
                },
                new QasFormatRootResponse()
                {
                    Address = new List <QasFormatAddressReponse>()
                    {
                        new QasFormatAddressReponse()
                        {
                            PostalCode = null
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine1 = "line1"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine2 = "line2"
                        },
                        new QasFormatAddressReponse()
                        {
                            AddressLine3 = "line3"
                        },
                        new QasFormatAddressReponse()
                        {
                            Locality = "loc"
                        }
                    }
                }
            };

            PostcodeDto result = qasMapper.MapToPostcodeDto(postCode, qasFormatRootResponses);

            Assert.AreEqual(1, result.AddressDetails.Count);
            Assert.AreEqual(expectedPostcode, result.Postcode);
            Assert.AreNotEqual(DateTime.MinValue, result.LastUpdated);

            AddressDetailsDto addressResult = result.AddressDetails.FirstOrDefault();

            Assert.AreEqual(expectedPostcode, addressResult.Postcode);
            Assert.AreEqual("line1", addressResult.AddressLine1);
            Assert.AreEqual("line2", addressResult.AddressLine2);
            Assert.AreEqual("line3", addressResult.AddressLine3);
            Assert.AreEqual("loc", addressResult.Locality);
            Assert.AreNotEqual(DateTime.MinValue, addressResult.LastUpdated);
        }
Exemplo n.º 7
0
        public async Task <IEnumerable <PostcodeDto> > GetPostCodesAndAddressesFromQasAsync(IEnumerable <string> missingPostcodes, CancellationToken cancellationToken)
        {
            // call QAS for missing postcodes and addresses
            List <Task <QasSearchRootResponse> > qasSearchResponseTasks = new List <Task <QasSearchRootResponse> >();
            List <QasSearchRootResponse>         qasSearchResponses     = new List <QasSearchRootResponse>();

            foreach (string missingPostcode in missingPostcodes)
            {
                Task <QasSearchRootResponse> qasResponseTask = _qasService.GetGlobalIntuitiveSearchResponseAsync(PostcodeFormatter.FormatPostcode(missingPostcode), cancellationToken);
                qasSearchResponseTasks.Add(qasResponseTask);
            }

            while (qasSearchResponseTasks.Count > 0)
            {
                Task <QasSearchRootResponse> finishedQasResponseTask = await Task.WhenAny(qasSearchResponseTasks);

                qasSearchResponseTasks.Remove(finishedQasResponseTask);
                QasSearchRootResponse qasSearchResponse = await finishedQasResponseTask;
                qasSearchResponses.Add(qasSearchResponse);
            }

            // call QAS for address details (grouped by postcode to avoid sending 1000s of request at once and to map a single PostcodeDto at a time)
            ILookup <string, string> missingQasFormatIdsGroupedByPostCode = _qasMapper.GetFormatIds(qasSearchResponses);
            List <PostcodeDto>       missingPostcodeDtos = new List <PostcodeDto>();

            foreach (IGrouping <string, string> missingQasFormatIds in missingQasFormatIdsGroupedByPostCode)
            {
                List <Task <QasFormatRootResponse> > qasFormatResponseTasks = new List <Task <QasFormatRootResponse> >();
                foreach (string missingQasFormatId in missingQasFormatIds)
                {
                    Task <QasFormatRootResponse> qasFormatResponseTask = _qasService.GetGlobalIntuitiveFormatResponseAsync(missingQasFormatId, cancellationToken);
                    qasFormatResponseTasks.Add(qasFormatResponseTask);
                }

                List <QasFormatRootResponse> qasFormatResponses = new List <QasFormatRootResponse>();

                while (qasFormatResponseTasks.Count > 0)
                {
                    Task <QasFormatRootResponse> finishedQasFormatResponseTask = await Task.WhenAny(qasFormatResponseTasks);

                    qasFormatResponseTasks.Remove(finishedQasFormatResponseTask);
                    QasFormatRootResponse qasFormatResponse = await finishedQasFormatResponseTask;
                    qasFormatResponses.Add(qasFormatResponse);
                }

                PostcodeDto missingPostcodeDtosForThisBatch = _qasMapper.MapToPostcodeDto(missingQasFormatIds.Key, qasFormatResponses);

                try
                {
                    //new addresses need a friendly name
                    _friendlyNameGenerator.GenerateFriendlyName(missingPostcodeDtosForThisBatch);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning("Error generating friendly name", ex);
                }

                missingPostcodeDtos.Add(missingPostcodeDtosForThisBatch);
            }

            if (missingPostcodeDtos.Any())
            {
                await _repository.SaveAddressesAndFriendlyNameAsync(missingPostcodeDtos);
            }

            return(missingPostcodeDtos);
        }
        /// <summary>
        /// Adds a friendly name to PostcodeDtos based on address details within the postcode
        /// This rapidly became a mess, I'm sorry :(
        /// </summary>
        /// <param name="postcodeDtos"></param>
        public void GenerateFriendlyName(PostcodeDto postcodeDto)
        {
            IEnumerable <AddressDetailsDto> details = postcodeDto.AddressDetails.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine1));

            if (details == null || !details.Any())
            {
                postcodeDto.FriendlyName = "";
                return; //may want a different default but seems better to resort to postcode only in unhandleable cases
            }

            int totalAddresses = details.Count();
            //Identify and return simple addresses
            double simplePostcodeFraction = (double)details.Where(x => string.IsNullOrWhiteSpace(x.AddressLine2)).Count() / totalAddresses;

            //Simple Address Block
            if (simplePostcodeFraction >= ThresholdForMost)
            {
                //From a test sample seems to happen fairly frequently
                //filtering down to only only entries in the "dominant" case
                // this should clear out the occasional building name / business in a residential postcode
                IEnumerable <string> firstLine = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine1))
                                                 .Select(x => x.AddressLine1);

                (string name, int count) = firstLine.RemoveNumbersAndSelectMostCommon();

                if ((double)count / totalAddresses >= ThresholdForMost)
                {
                    string numberRange = firstLine.ExtractNumbers(name)
                                         .GenerateNumbersDescriptor(5);
                    postcodeDto.FriendlyName = numberRange + name;
                    return;
                }
            }

            //simple flat block
            //doesn't seem to be another way to identify a block of flats easily
            double simpleFlatFraction = (double)details.Where(x =>
                                                              x.AddressLine1.Contains("Flat") ||
                                                              x.AddressLine1.Contains("Apartment") ||
                                                              x.AddressLine1.All(char.IsDigit)
                                                              ).Count()
                                        / totalAddresses;

            if (simpleFlatFraction >= ThresholdForMost)
            {
                string result = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine2))
                                .Select(y => y.AddressLine2)
                                .GroupBy(z => z)
                                .OrderByDescending(a => a.Count())
                                .First()
                                .Key;

                // see if we can add a street name too
                double hasThirdLineFraction = (double)details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine3))
                                              .Count()
                                              / totalAddresses;

                if (hasThirdLineFraction > ThresholdForMost)
                {
                    (string nameLine3, int countLine3) = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine3))
                                                         .Select(y => y.AddressLine3)
                                                         .RemoveNumbersAndSelectMostCommon();

                    if ((double)countLine3 / totalAddresses > ThresholdForMost)
                    {
                        result = string.Concat(result, ", ", nameLine3);
                    }
                }

                postcodeDto.FriendlyName = result;
                return;
            }

            //case is neither a simple flat nor a simple street address - now it gets more difficult
            //most likely scenario is "# street name, district"
            //Hence move through each address line at a time, stripping out numbers and checking if the count meets the tolerance criteria
            //hopefully all of the flats have been caught by this out, and non-residential addresses should only match on street name
            IEnumerable <string> addressLine1 = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine1))
                                                .Select(y => y.AddressLine1);

            (string line1Name, int line1Count) = addressLine1.RemoveNumbersAndSelectMostCommon();

            if ((double)line1Count / totalAddresses > ThresholdForMost)
            {
                string numberRange = addressLine1.ExtractNumbers(line1Name)
                                     .GenerateNumbersDescriptor(5);
                //probably of the farm "# street name, region, locality"
                postcodeDto.FriendlyName = numberRange + line1Name;
                return;
            }

            IEnumerable <string> addressLine2 = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine2))
                                                .Select(y => y.AddressLine2);

            (string line2Name, int line2Count) = addressLine2.RemoveNumbersAndSelectMostCommon();

            if ((double)line2Count / totalAddresses > ThresholdForMost)
            {
                string numberRange = addressLine2.ExtractNumbers(line2Name)
                                     .GenerateNumbersDescriptor(5);
                //probably non residential addresses of the form "business name, # street name"
                postcodeDto.FriendlyName = numberRange + line2Name;
                return;
            }

            IEnumerable <string> addressLine3 = details.Where(x => !string.IsNullOrWhiteSpace(x.AddressLine3))
                                                .Select(y => y.AddressLine3);

            (string line3Name, int line3Count) = addressLine3.RemoveNumbersAndSelectMostCommon();
            if ((double)line3Count / totalAddresses > ThresholdForMost)
            {
                string numberRange = addressLine3.ExtractNumbers(line3Name)
                                     .GenerateNumbersDescriptor(5);
                //weird edge cases with building names and buisness names on line 1 and 2
                //should pull streets out of line 3
                postcodeDto.FriendlyName = numberRange + line3Name;
                return;
            }

            //Everything has failed :(
            postcodeDto.FriendlyName = "";

            return;
        }
 public void Setup()
 {
     _postcodeDto = new PostcodeDto();
 }