private void SetupTestData(
     out IReadOnlyCollection<Break> breaksBeingSmoothed,
     out SmoothBreak theSmoothBreak,
     out IReadOnlyCollection<Programme> scheduleProgrammes,
     out List<Spot> spotsForBreak,
     out SalesArea salesArea,
     out IReadOnlyDictionary<string, Clash> clashesByExternalRef,
     out IReadOnlyDictionary<Guid, SpotInfo> spotInfos,
     out ProductClashRules productClashRule,
     out SmoothResources smoothResources,
     out IClashExposureCountService clashExposureCountService,
     out SponsorshipRestrictionService sponsorshipRestrictionsService)
 {
     breaksBeingSmoothed = _repositoryWrapper.LoadAllTestBreaks().ToList();
     theSmoothBreak = new SmoothBreak(breaksBeingSmoothed.First(), 1);
     scheduleProgrammes = _repositoryWrapper.LoadAllProgrammes().ToList();
     spotsForBreak = new List<Spot>() { _spot };
     salesArea = _repositoryWrapper.LoadAllSalesArea().First();
     clashesByExternalRef = _repositoryWrapper.LoadAllClashes().ToDictionary(c => c.Externalref);
     spotInfos = SpotInfo.Factory(
         spotsForBreak,
         _repositoryWrapper.LoadAllProducts().ToDictionary(p => p.Externalidentifier),
         clashesByExternalRef
         );
     productClashRule = ProductClashRules.LimitOnExposureCount;
     smoothResources = new SmoothResources();
     clashExposureCountService = ClashExposureCountService.Create();
     sponsorshipRestrictionsService = SponsorshipRestrictionService.Factory(
         spotInfos,
         new SponsorshipRestrictionFilterService(ImmutableList.Create<Sponsorship>()),
         new SmoothSponsorshipTimelineManager(new List<SmoothSponsorshipTimeline>()),
         scheduleProgrammes.First(),
         DebugLogger);
 }
        public void AddSponsoredSpot()
        {
            // Arrange
            Spot spot = GetSponsoredSpot();

            _service = new SponsorshipRestrictionService(
                GetSponsorshipRestrictionFilterResults(
                    SponsorshipApplicability.AllCompetitors,
                    SponsorshipRestrictionType.SpotCount,
                    5,
                    SponsorshipCalculationType.Percentage),
                _mockTimeslines.Object,
                GetSpotInfo(),
                null);

            // Act
            _service.RaiseAddedSpotToBreakEvent += RaiseAddedSpotToBreakEvent;
            _service.TriggerRecalculationOfAllowedRestrictionLimits(AddAction, spot, _theBreak);
Esempio n. 3
0
        /// <summary>
        /// Create a new instance of this class for each programme to smooth.
        /// </summary>
        public SmoothOneProgramme(
            Programme programme,
            IEnumerable <Break> programmeBreaks,
            IReadOnlyCollection <Spot> programmeSpots,
            IReadOnlyDictionary <Guid, SpotInfo> spotInfos,
            Guid runId,
            SalesArea salesArea,
            DateTime processorDateTime,
            ISmoothDiagnostics smoothDiagnostics,
            IImmutableList <RatingsPredictionSchedule> ratingsPredictionSchedules,
            ImmutableSmoothData threadSafeCollections,
            IClashExposureCountService clashExposureCountService,
            SponsorshipRestrictionService sponsorshipRestrictionService,
            IReadOnlyCollection <Product> products,
            IReadOnlyCollection <Clash> clashes,
            IReadOnlyCollection <Programme> allProgrammesForPeriodAndSalesArea,
            Action <string> raiseInfo,
            Action <string, Exception> raiseException)
        {
            RaiseInfo      = raiseInfo;
            RaiseException = raiseException;

            if (programme is null)
            {
                var guruMeditation = new ArgumentNullException(nameof(programme));
                RaiseException("The programme to Smooth is null", guruMeditation);

                throw guruMeditation;
            }

            _smoothProgramme = new SmoothProgramme(salesArea, programme);
            _smoothProgramme.InitialiseSmoothBreaks(programmeBreaks);

            _processorDateTime             = processorDateTime;
            _programmeSpots                = programmeSpots;
            _spotInfos                     = spotInfos;
            _runId                         = runId;
            _threadSafeCollections         = threadSafeCollections;
            _sponsorshipRestrictionService = sponsorshipRestrictionService;
            _smoothConfiguration           = threadSafeCollections.SmoothConfigurationReader;
            _smoothDiagnostics             = smoothDiagnostics;

            _smoothPassBookedExecuter = new SmoothPassBookedExecuter(
                _smoothDiagnostics,
                _smoothResources,
                _smoothProgramme,
                sponsorshipRestrictionService,
                allProgrammesForPeriodAndSalesArea,
                RaiseInfo,
                RaiseException);

            _smoothPassDefaultExecuter = new SmoothPassDefaultExecuter(
                _smoothDiagnostics,
                _smoothResources,
                _smoothProgramme,
                sponsorshipRestrictionService,
                allProgrammesForPeriodAndSalesArea,
                _smoothConfiguration,
                clashExposureCountService,
                _threadSafeCollections.ClashesByExternalRef,
                _threadSafeCollections.ProductsByExternalRef,
                RaiseException);

            _smoothPassUnplacedExecuter = new SmoothPassUnplacedExecuter(
                _smoothDiagnostics,
                _smoothResources,
                _smoothProgramme,
                sponsorshipRestrictionService,
                allProgrammesForPeriodAndSalesArea,
                _smoothConfiguration,
                clashExposureCountService,
                _threadSafeCollections.ClashesByExternalRef,
                RaiseException);

            if (_smoothConfiguration.ClashExceptionCheckEnabled)
            {
                _smoothResources.ClashExceptionChecker = new ClashExceptionChecker(
                    threadSafeCollections.ClashExceptions,
                    products,
                    clashes
                    );
            }

            if (_smoothConfiguration.RestrictionCheckEnabled)
            {
                _smoothResources.RestrictionChecker = new RestrictionChecker(
                    threadSafeCollections.Restrictions,
                    products,
                    clashes,
                    threadSafeCollections.IndexTypes,
                    threadSafeCollections.Universes,
                    ratingsPredictionSchedules);
            }

            _smoothFailuresFactory        = new SmoothFailuresFactory(_smoothConfiguration);
            _smoothRecommendationsFactory = new SmoothRecommendationsFactory(_smoothConfiguration);
        }
Esempio n. 4
0
        Execute(DateTimeRange weekBeingSmoothed)
        {
            string salesAreaName = _salesArea.Name;

            string auditMessageForSalesAreaNameAndBatchStartEndDate =
                $"for sales area {salesAreaName} ({Log(weekBeingSmoothed)})";

            RaiseInfoAndDebug($"Smoothing batch {auditMessageForSalesAreaNameAndBatchStartEndDate}");

            var batchThreadOutput = new SmoothBatchOutput();

            foreach (var item in PrepareSmoothFailureMessageCollection(_threadSafeCollections.SmoothFailureMessages))
            {
                batchThreadOutput.SpotsByFailureMessage.Add(item);
            }

            foreach (var item in PrepareSmoothOutputWithSmoothPasses(_smoothPasses))
            {
                batchThreadOutput.OutputByPass.Add(item);
            }

            int countBreaksWithPreviousSpots   = 0;
            int countBreaksWithNoPreviousSpots = 0;

            var programmes = new List <Programme>();
            var allSpots   = new List <Spot>();
            var spots      = new List <Spot>();

            IImmutableList <Break> breaksForTheWeekBeingSmoothed = ImmutableList <Break> .Empty;
            IImmutableList <RatingsPredictionSchedule> ratingsPredictionSchedules = ImmutableList <RatingsPredictionSchedule> .Empty;

            IReadOnlyDictionary <string, SpotPlacement> spotPlacementsByExternalRef;

            try
            {
#pragma warning disable HAA0101 // Array allocation for params parameter
                Parallel.Invoke(
                    () => programmes.AddRange(LoadProgrammesToSmooth(weekBeingSmoothed, salesAreaName)),
                    () =>
                {
                    var results = LoadSpotsToSmooth(weekBeingSmoothed, salesAreaName);
                    allSpots.AddRange(results.allSpots);
                    spots.AddRange(results.unbookedSpots);
                },
                    () => ratingsPredictionSchedules    = LoadRatingsPredictionSchedules(weekBeingSmoothed, salesAreaName),
                    () => breaksForTheWeekBeingSmoothed = LoadBreaksToSmooth(weekBeingSmoothed, salesAreaName)
                    );
#pragma warning restore HAA0101 // Array allocation for params parameter

                void RaiseInfoForCounts(string message) =>
                RaiseInfo($"Read {message} {auditMessageForSalesAreaNameAndBatchStartEndDate}");

                RaiseInfoForCounts($"{Log(programmes.Count)} programmes");
                RaiseInfoForCounts($"{Log(allSpots.Count)} client picked spots");
                RaiseInfoForCounts($"{Log(spots.Count)} unbooked spots");
                RaiseInfoForCounts($"{Log(ratingsPredictionSchedules.Count)} ratings prediction schedules");
                RaiseInfoForCounts($"{Log(breaksForTheWeekBeingSmoothed.Count)} breaks");

                spotPlacementsByExternalRef = LoadSmoothPreviousSpotPlacements(allSpots);
            }
            catch (Exception exception)
            {
                throw new Exception(
                          $"Error loading smooth data {auditMessageForSalesAreaNameAndBatchStartEndDate})",
                          exception
                          );
            }

            VerifyModel.VerifySpotProductsReferences(
                salesAreaName,
                allSpots,
                _threadSafeCollections.ProductsByExternalRef,
                _threadSafeCollections.ClashesByExternalRef,
                RaiseWarning
                );

            // Default all spots to no placement attempt, will reduce
            // the list as we attempt, generate Smooth failures at the
            // end for any where no attempt was made to place it (E.g.
            // no breaks or progs)
            IReadOnlyDictionary <Guid, SpotInfo> spotInfos = SpotInfo.Factory(
                allSpots,
                _threadSafeCollections.ProductsByExternalRef,
                _threadSafeCollections.ClashesByExternalRef
                );

            IReadOnlyDictionary <string, Break> breaksByExternalRef =
                Break.IndexListByExternalId(breaksForTheWeekBeingSmoothed);

            var filterService = new SponsorshipRestrictionFilterService(
                _threadSafeCollections.SponsorshipRestrictions);

            IReadOnlyList <SmoothSponsorshipTimeline> timelines = filterService
                                                                  .GetSponsorshipRestrictionTimeline(
                weekBeingSmoothed,
                salesAreaName);

            ISmoothSponsorshipTimelineManager timelineManager = new SmoothSponsorshipTimelineManager(timelines);
            timelineManager.SetupTimelineRunningTotals(breaksForTheWeekBeingSmoothed);

            IReadOnlyCollection <Product> products = _threadSafeCollections
                                                     .ProductsByExternalRef.Values.ToList();

            IReadOnlyCollection <Clash> clashes = _threadSafeCollections
                                                  .ClashesByExternalRef.Values.ToList();

            var smoothFailures      = new List <SmoothFailure>();
            var recommendations     = new List <Recommendation>();
            var spotIdsUsedForBatch = new HashSet <Guid>();

            foreach (Programme oneProgramme in programmes.OrderBy(p => p.StartDateTime))
            {
                var(programmeBreaks, programmeSpots) = GetProgrammeBreaksAndSpots(
                    oneProgramme,
                    allSpots,
                    breaksForTheWeekBeingSmoothed);

                // The running total and maximums allowed will add up
                // within the service as we can't access the variables
                // outside of the programme loop from inside the event
                // handlers (they're in a different scope).
                var sponsorshipRestrictionService =
                    SponsorshipRestrictionService.Factory(
                        spotInfos,
                        filterService,
                        timelineManager,
                        oneProgramme,
                        RaiseException);

                var smoothOneProgramme = new SmoothOneProgramme(
                    oneProgramme,
                    programmeBreaks,
                    programmeSpots,
                    spotInfos,
                    _runId,
                    _salesArea,
                    _processorDateTime,
                    _smoothDiagnostics,
                    ratingsPredictionSchedules,
                    _threadSafeCollections,
                    _clashExposureCountService,
                    sponsorshipRestrictionService,
                    products,
                    clashes,
                    programmes,
                    RaiseInfo,
                    RaiseException);

                var smoothProgramme = smoothOneProgramme.Execute(
                    batchThreadOutput,
                    _smoothPasses,
                    breaksByExternalRef,
                    spotPlacementsByExternalRef,
                    breaksForTheWeekBeingSmoothed,
                    spotIdsUsedForBatch);

                countBreaksWithPreviousSpots   += smoothProgramme.BreaksWithPreviousSpots;
                countBreaksWithNoPreviousSpots += smoothProgramme.BreaksWithoutPreviousSpots;

                _saveSmoothChanges.SaveSmoothedSpots(smoothProgramme.SpotsToBatchSave);

                smoothFailures.AddRange(smoothProgramme.SmoothFailures);
                recommendations.AddRange(smoothProgramme.Recommendations);

                bool SpotNotSetDueToExternalCampaignRef(Spot s) =>
                _smoothConfiguration.ExternalCampaignRefsToExclude.Contains(s.ExternalCampaignNumber) &&
                !s.IsBooked();

                batchThreadOutput.SpotsNotSetDueToExternalCampaignRef +=
                    programmeSpots.Count(SpotNotSetDueToExternalCampaignRef);

                // Copy the final values of the sponsorship restriction
                // calculations and running totals into the outer
                // variables so the next iteration can use them. This
                // seems to be the only way to do this at the minute,
                // until I think of something else.
                timelineManager = sponsorshipRestrictionService.TimelineManager;
            } // End of foreach programme loop

            string batchStartEndDateForLogging = Log(weekBeingSmoothed);

            var recommendationsForUnplacedSpots = _smoothRecommendationsFactory
                                                  .CreateRecommendationsForUnplacedSpots(
                allSpots,
                _salesArea,
                _processorDateTime
                );

            RaiseInfo(
                $"Created {Log(recommendationsForUnplacedSpots.Count)} recommendations " +
                $"for unplaced spots {auditMessageForSalesAreaNameAndBatchStartEndDate}");

            recommendations.AddRange(recommendationsForUnplacedSpots);
            batchThreadOutput.Recommendations += recommendations.Count;

            _saveSmoothChanges.SaveSpotPlacements(
                _processorDateTime,
                allSpots,
                spotPlacementsByExternalRef,
                spotInfos,
                batchStartEndDateForLogging
                );

            _saveSmoothChanges.SaveSmoothFailures(
                _runId,
                salesAreaName,
                _smoothFailuresFactory,
                smoothFailures,
                spots,
                spotInfos,
                batchStartEndDateForLogging
                );

            UpdateSmoothOutputForSpotsByFailureMessage(
                batchThreadOutput.SpotsByFailureMessage,
                smoothFailures,
                batchStartEndDateForLogging
                );

            batchThreadOutput.Failures += smoothFailures.Count;

            var unusedSpotIdsForBatch = new HashSet <Guid>();
            AddUnusedSpotsToUnusedSpotsCollection(spotIdsUsedForBatch, unusedSpotIdsForBatch, allSpots);

            spotIdsUsedForBatch.CopyDistinctTo(batchThreadOutput.UsedSpotIds);
            unusedSpotIdsForBatch.CopyDistinctTo(batchThreadOutput.UnusedSpotIds);

            _saveSmoothChanges.SaveSmoothRecommendations(
                _firstScenarioId,
                auditMessageForSalesAreaNameAndBatchStartEndDate,
                recommendations
                );

            RaiseInfo(
                $"Smoothed batch {auditMessageForSalesAreaNameAndBatchStartEndDate}: " +
                $"Breaks with no prev spots={countBreaksWithNoPreviousSpots.ToString()}, " +
                $"Breaks with prev spots={countBreaksWithPreviousSpots.ToString()})"
                );

            return(batchThreadOutput, recommendations, smoothFailures);
        }