private async Task StartSearch(Guid searchId, AvailabilityRequest request, AccommodationBookingSettings searchSettings, Dictionary <Suppliers, List <SupplierCodeMapping> > accommodationCodes, AgentContext agent, string languageCode)
        {
            foreach (var supplier in searchSettings.EnabledConnectors)
            {
                if (!accommodationCodes.TryGetValue(supplier, out var supplierCodeMappings))
                {
                    await _stateStorage.SaveState(searchId, SupplierAvailabilitySearchState.Completed(searchId, new List <string>(0), 0), supplier);

                    continue;
                }

                // Starting search tasks in a separate thread
                StartSearchTask(supplier, supplierCodeMappings);
            }


            void StartSearchTask(Suppliers supplier, List <SupplierCodeMapping> supplierCodeMappings)
            {
                Task.Run(async() =>
                {
                    using var scope = _serviceScopeFactory.CreateScope();

                    await WideAvailabilitySearchTask
                    .Create(scope.ServiceProvider)
                    .Start(searchId, request, supplierCodeMappings, supplier, agent, languageCode, searchSettings);
                });
            }
        }
Example #2
0
        public static List <RoomContractSet> ApplySearchFilters(this IEnumerable <RoomContractSet> roomContractSets,
                                                                AccommodationBookingSettings searchSettings, IDateTimeProvider dateTimeProvider, DateTime checkInDate)
        {
            return(roomContractSets.Where(roomSet =>
            {
                if (searchSettings.AprMode == AprMode.Hide && roomSet.IsAdvancePurchaseRate)
                {
                    return false;
                }

                if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
                {
                    var tomorrow = dateTimeProvider.UtcTomorrow();
                    if (checkInDate <= tomorrow)
                    {
                        return false;
                    }

                    var deadlineDate = roomSet.Deadline.Date;
                    if (deadlineDate.HasValue && deadlineDate.Value.Date <= tomorrow)
                    {
                        return false;
                    }
                }

                return true;
            })
                   .ToList());
        }
        private void StartSearchTasks(Guid searchId, AvailabilityRequest request, AccommodationBookingSettings searchSettings, Location location, AgentContext agent, string languageCode)
        {
            var contractsRequest = ConvertRequest(request, location);

            foreach (var supplier in GetSuppliersToSearch(location, searchSettings.EnabledConnectors))
            {
                Task.Run(async() =>
                {
                    using var scope = _serviceScopeFactory.CreateScope();

                    await WideAvailabilitySearchTask
                    .Create(scope.ServiceProvider)
                    .Start(searchId, contractsRequest, supplier, agent, languageCode, searchSettings);
                });
            }


            IReadOnlyCollection <Suppliers> GetSuppliersToSearch(in Location location, List <Suppliers> suppliers)
            {
                return(location.Suppliers != null && location.Suppliers.Any()
                    ? location.Suppliers.Intersect(suppliers).ToList()
                    : suppliers);
            }
Example #4
0
        public async Task Start(Guid searchId, AvailabilityRequest request, Suppliers supplier, AgentContext agent, string languageCode,
                                AccommodationBookingSettings searchSettings)
        {
            var supplierConnector = _supplierConnectorManager.Get(supplier);

            try
            {
                _logger.LogProviderAvailabilitySearchStarted($"Availability search with id '{searchId}' on supplier '{supplier}' started");

                await GetAvailability(request, languageCode)
                .Bind(ConvertCurrencies)
                .Map(ApplyMarkups)
                .Map(Convert)
                .Tap(SaveResult)
                .Finally(SaveState);
            }
            catch (Exception ex)
            {
                // TODO: Add sentry error notification
                _logger.LogProviderAvailabilitySearchException(ex);
                var result = ProblemDetailsBuilder.Fail <List <AccommodationAvailabilityResult> >("Server error", HttpStatusCode.InternalServerError);
                await SaveState(result);
            }


            async Task <Result <EdoContracts.Accommodations.Availability, ProblemDetails> > GetAvailability(AvailabilityRequest request,
                                                                                                            string languageCode)
            {
                var saveToStorageTask   = _storage.SaveState(searchId, SupplierAvailabilitySearchState.Pending(searchId), supplier);
                var getAvailabilityTask = supplierConnector.GetAvailability(request, languageCode);
                await Task.WhenAll(saveToStorageTask, getAvailabilityTask);

                return(getAvailabilityTask.Result);
            }

            async Task <Result <EdoContracts.Accommodations.Availability, ProblemDetails> > ConvertCurrencies(EdoContracts.Accommodations.Availability availabilityDetails)
            {
                var convertedResults = new List <SlimAccommodationAvailability>(availabilityDetails.Results.Count);

                foreach (var slimAccommodationAvailability in availabilityDetails.Results)
                {
                    // Currency can differ in different results
                    var(_, isFailure, convertedAccommodationAvailability, error) = await _priceProcessor.ConvertCurrencies(agent, slimAccommodationAvailability, AvailabilityResultsExtensions.ProcessPrices,
                                                                                                                           AvailabilityResultsExtensions.GetCurrency);

                    if (isFailure)
                    {
                        return(Result.Failure <EdoContracts.Accommodations.Availability, ProblemDetails>(error));
                    }

                    convertedResults.Add(convertedAccommodationAvailability);
                }

                return(new EdoContracts.Accommodations.Availability(availabilityDetails.AvailabilityId, availabilityDetails.NumberOfNights,
                                                                    availabilityDetails.CheckInDate, availabilityDetails.CheckOutDate, convertedResults, availabilityDetails.NumberOfProcessedAccommodations));
            }

            async Task <EdoContracts.Accommodations.Availability> ApplyMarkups(EdoContracts.Accommodations.Availability response)
            {
                var markup = await _priceProcessor.ApplyMarkups(agent, response, AvailabilityResultsExtensions.ProcessPrices);

                return(markup.Data);
            }

            async Task <List <AccommodationAvailabilityResult> > Convert(EdoContracts.Accommodations.Availability details)
            {
                var supplierAccommodationIds = details.Results
                                               .Select(r => new SupplierAccommodationId(supplier, r.Accommodation.Id))
                                               .Distinct()
                                               .ToList();

                var duplicates = await _duplicatesService.GetDuplicateReports(supplierAccommodationIds);

                var timestamp = _dateTimeProvider.UtcNow().Ticks;

                return(details
                       .Results
                       .Select(accommodationAvailability =>
                {
                    var minPrice = accommodationAvailability.RoomContractSets.Min(r => r.Rate.FinalPrice.Amount);
                    var maxPrice = accommodationAvailability.RoomContractSets.Max(r => r.Rate.FinalPrice.Amount);
                    var accommodationId = new SupplierAccommodationId(supplier, accommodationAvailability.Accommodation.Id);
                    var resultId = Guid.NewGuid();
                    var duplicateReportId = duplicates.TryGetValue(accommodationId, out var reportId)
                            ? reportId
                            : string.Empty;
                    var roomContractSets = accommodationAvailability.RoomContractSets
                                           .ToRoomContractSetList()
                                           .ApplySearchFilters(searchSettings, _dateTimeProvider, request.CheckInDate);

                    return new AccommodationAvailabilityResult(resultId,
                                                               timestamp,
                                                               details.AvailabilityId,
                                                               accommodationAvailability.Accommodation,
                                                               roomContractSets,
                                                               duplicateReportId,
                                                               minPrice,
                                                               maxPrice,
                                                               request.CheckInDate,
                                                               request.CheckOutDate);
                })
                       .Where(a => a.RoomContractSets.Any())
                       .ToList());
            }

            Task SaveResult(List <AccommodationAvailabilityResult> results) => _storage.SaveResults(searchId, supplier, results);


            Task SaveState(Result <List <AccommodationAvailabilityResult>, ProblemDetails> result)
            {
                var state = result.IsSuccess
                    ? SupplierAvailabilitySearchState.Completed(searchId, result.Value.Select(r => r.DuplicateReportId).ToList(), result.Value.Count)
                    : SupplierAvailabilitySearchState.Failed(searchId, result.Error.Detail);

                if (state.TaskState == AvailabilitySearchTaskState.Completed)
                {
                    _logger.LogProviderAvailabilitySearchSuccess(
                        $"Availability search with id '{searchId}' on supplier '{supplier}' finished successfully with '{state.ResultCount}' results");
                }
                else
                {
                    _logger.LogProviderAvailabilitySearchFailure(
                        $"Availability search with id '{searchId}' on supplier '{supplier}' finished with state '{state.TaskState}', error '{state.Error}'");
                }

                return(_storage.SaveState(searchId, state, supplier));
            }
        }
        public async Task <List <WideAvailabilityResult> > GetFilteredResults(Guid searchId, AvailabilitySearchFilter filters, AccommodationBookingSettings searchSettings, List <Suppliers> suppliers, string languageCode)
        {
            var rows = await _availabilityStorage.Collection()
                       .Where(r => r.SearchId == searchId && suppliers.Contains(r.Supplier))
                       .Select(r => new { r.Id, r.HtId, r.Created })
                       .ToListAsync();

            var htIds = new List <string>();
            var ids   = new List <ObjectId>();

            foreach (var group in rows.GroupBy(r => r.HtId))
            {
                htIds.Add(group.Key);
                ids.Add(group.OrderBy(g => g.Created).First().Id);
            }

            await _accommodationsStorage.EnsureAccommodationsCached(htIds, languageCode);

            var query = _availabilityStorage.Collection()
                        .Where(r => r.SearchId == searchId && ids.Contains(r.Id));

            query = filters.Suppliers is not null
                ? query.Where(r => filters.Suppliers.Contains(r.Supplier))
                : query.Where(r => suppliers.Contains(r.Supplier));

            if (filters.MinPrice is not null)
            {
                query = query.Where(r => r.MinPrice >= filters.MinPrice);
            }

            if (filters.MaxPrice is not null)
            {
                query = query.Where(r => r.MaxPrice <= filters.MaxPrice);
            }

            if (filters.BoardBasisTypes is not null)
            {
                query = query.Where(r => r.RoomContractSets.Any(rcs => rcs.Rooms.Any(room => filters.BoardBasisTypes.Contains(room.BoardBasis))));
            }

            if (searchSettings.AprMode == AprMode.Hide)
            {
                query = query.Where(r => r.RoomContractSets.Any(rcs => !rcs.IsAdvancePurchaseRate));
            }

            if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
            {
                query = query.Where(r => r.RoomContractSets.Any(rcs => rcs.Deadline.Date == null || rcs.Deadline.Date >= _dateTimeProvider.UtcNow()));
            }

            if (filters.Ratings is not null)
            {
                var filteredHtIds = await GetAccommodationRatings(htIds, filters.Ratings);

                query = query.Where(r => filteredHtIds.Contains(r.HtId));
            }

            if (filters.Order == "price")
            {
                query = filters.Direction switch
                {
                    "asc" => query.OrderBy(x => x.MinPrice),
                    "desc" => query.OrderByDescending(x => x.MinPrice)
                };
            }
            else
            {
                query = query
                        .OrderBy(r => r.Created)
                        .ThenBy(r => r.HtId);
            }

            var results = await query
                          .Skip(filters.Skip)
                          .Take(filters.Top)
                          .ToListAsync();

            return(results
                   .Select(a =>
            {
                var accommodation = _accommodationsStorage.GetAccommodation(a.HtId, languageCode);
                var roomContractSets = a.RoomContractSets
                                       .Select(r => r.ApplySearchSettings(searchSettings.IsSupplierVisible, searchSettings.IsDirectContractFlagVisible))
                                       .ToList();

                if (searchSettings.AprMode == AprMode.Hide)
                {
                    roomContractSets = roomContractSets.Where(r => !r.IsAdvancePurchaseRate).ToList();
                }

                if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
                {
                    roomContractSets = roomContractSets.Where(r => r.Deadline.Date == null || r.Deadline.Date >= _dateTimeProvider.UtcNow()).ToList();
                }

                return new WideAvailabilityResult(accommodation: accommodation,
                                                  roomContractSets: roomContractSets,
                                                  minPrice: roomContractSets.Min(r => r.Rate.FinalPrice.Amount),
                                                  maxPrice: roomContractSets.Max(r => r.Rate.FinalPrice.Amount),
                                                  checkInDate: a.CheckInDate,
                                                  checkOutDate: a.CheckOutDate,
                                                  supplier: searchSettings.IsSupplierVisible
                            ? a.Supplier
                            : null,
                                                  htId: a.HtId);
            }).ToList());
        }
Example #6
0
        public async Task <List <WideAvailabilityResult> > GetFilteredResults(Guid searchId, AvailabilitySearchFilter filters, AccommodationBookingSettings searchSettings, List <Suppliers> suppliers, string languageCode)
        {
            var results = await GetResults(searchId, suppliers);

            var availabilities = results.SelectMany(r => r.AccommodationAvailabilities)
                                 .GroupBy(a => a.HtId)
                                 .Select(g => g.OrderBy(a => a.Created).First())
                                 .AsQueryable();

            if (searchSettings.AprMode == AprMode.Hide)
            {
                availabilities = availabilities.Where(a => a.RoomContractSets.All(rcs => !rcs.IsAdvancePurchaseRate));
            }

            if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
            {
                availabilities = availabilities.Where(a => a.RoomContractSets.All(rcs => rcs.Deadline.Date == null || rcs.Deadline.Date >= DateTime.UtcNow));
            }

            return(availabilities
                   .OrderBy(a => a.Created)
                   .ThenBy(a => a.HtId)
                   .ToWideAvailabilityResults(searchSettings));
        }
Example #7
0
        public async Task <List <WideAvailabilityResult> > GetFilteredResults(Guid searchId, AvailabilitySearchFilter filters, AccommodationBookingSettings searchSettings, List <Suppliers> suppliers, string languageCode)
        {
            var rows = await _availabilityStorage.Collection()
                       .Where(r => r.SearchId == searchId && suppliers.Contains(r.Supplier))
                       .Select(r => new { r.Id, r.HtId, r.Created })
                       .ToListAsync();

            var ids = rows.GroupBy(r => r.HtId)
                      .Select(group => group.OrderBy(g => g.Created).First().Id)
                      .ToList();

            var query = _availabilityStorage.Collection()
                        .Where(r => r.SearchId == searchId && ids.Contains(r.Id));

            if (searchSettings.AprMode == AprMode.Hide)
            {
                query = query.Where(r => r.RoomContractSets.Any(rcs => !rcs.IsAdvancePurchaseRate));
            }

            if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
            {
                query = query.Where(r => r.RoomContractSets.Any(rcs => rcs.Deadline.Date == null || rcs.Deadline.Date >= DateTime.UtcNow));
            }

            return((await query
                    .OrderBy(r => r.Created)
                    .ThenBy(r => r.HtId)
                    .ToListAsync())
                   .ToWideAvailabilityResults(searchSettings));
        }
Example #8
0
        public async Task <List <WideAvailabilityResult> > GetFilteredResults(Guid searchId, AvailabilitySearchFilter filters, AccommodationBookingSettings searchSettings, List <Suppliers> suppliers, string languageCode)
        {
            var results = await GetResults(searchId, suppliers);

            var availabilities = results.SelectMany(r => r.AccommodationAvailabilities)
                                 .GroupBy(a => a.HtId)
                                 .Select(g => g.OrderBy(a => a.Created).First())
                                 .AsQueryable();

            if (searchSettings.AprMode == AprMode.Hide)
            {
                availabilities = availabilities.Where(a => a.RoomContractSets.All(rcs => !rcs.IsAdvancePurchaseRate));
            }

            if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
            {
                availabilities = availabilities.Where(a => a.RoomContractSets.All(rcs => rcs.Deadline.Date == null || rcs.Deadline.Date >= _dateTimeProvider.UtcNow()));
            }

            var htIds = availabilities.Select(a => a.HtId).ToList();
            await _accommodationsStorage.EnsureAccommodationsCached(htIds, languageCode);

            var queriable = availabilities
                            .OrderBy(a => a.Created)
                            .ThenBy(a => a.HtId)
                            .ToList()
                            .Select(a =>
            {
                var accommodation = _accommodationsStorage.GetAccommodation(a.HtId, languageCode);

                return(new WideAvailabilityResult(accommodation: accommodation,
                                                  roomContractSets: a.RoomContractSets.Select(r => r.ApplySearchSettings(searchSettings.IsSupplierVisible, searchSettings.IsDirectContractFlagVisible)).ToList(),
                                                  minPrice: a.RoomContractSets.Min(r => r.Rate.FinalPrice.Amount),
                                                  maxPrice: a.RoomContractSets.Max(r => r.Rate.FinalPrice.Amount),
                                                  checkInDate: a.CheckInDate,
                                                  checkOutDate: a.CheckOutDate,
                                                  supplier: searchSettings.IsSupplierVisible
                            ? a.Supplier
                            : null,
                                                  htId: a.HtId));
            })
                            .AsQueryable();

            return(filters.ApplyTo(queriable).ToList());
        }
        private static WideAvailabilityResult ToWideAvailabilityResult(this AccommodationAvailabilityResult result, AccommodationBookingSettings searchSettings)
        {
            var roomContractSets = result.RoomContractSets
                                   .Select(r => r.ApplySearchSettings(searchSettings.IsSupplierVisible, searchSettings.IsDirectContractFlagVisible))
                                   .ToList();

            if (searchSettings.AprMode == AprMode.Hide)
            {
                roomContractSets = roomContractSets.Where(r => !r.IsAdvancePurchaseRate).ToList();
            }

            if (searchSettings.PassedDeadlineOffersMode == PassedDeadlineOffersMode.Hide)
            {
                roomContractSets = roomContractSets.Where(r => r.Deadline.Date == null || r.Deadline.Date >= DateTime.UtcNow).ToList();
            }

            return(new WideAvailabilityResult(accommodation: default,
 public static List <WideAvailabilityResult> ToWideAvailabilityResults(this IEnumerable <AccommodationAvailabilityResult> list, AccommodationBookingSettings searchSettings)
 => list.Select(x => x.ToWideAvailabilityResult(searchSettings)).ToList();