예제 #1
0
        /// <summary>
        /// A pass for Booked spots. Don't store results, no spots placed.
        /// </summary>
        private void ExecuteBookedPass(
            SmoothPassBooked specificSmoothPass,
            IReadOnlyCollection <Break> breaksForThePeriodBeingSmoothed,
            ISet <Guid> spotIdsUsed,
            SmoothBatchOutput smoothBatchOutput)
        {
            SmoothPassResult result = _smoothPassBookedExecuter.Execute(
                specificSmoothPass,
                breaksForThePeriodBeingSmoothed,
                spotIdsUsed);

            smoothBatchOutput.BookedSpotsUnplacedDueToRestrictions +=
                result.BookedSpotIdsUnplacedDueToRestrictions.Count;
        }
예제 #2
0
        private void ExecuteSmoothPasses(
            IEnumerable <SmoothPass> smoothPasses,
            IReadOnlyCollection <Break> breaksForThePeriodBeingSmoothed,
            SmoothBatchOutput smoothBatchOutput,
            List <Spot> progSpotsNotUsed,
            List <SmoothPassResult> smoothPassResults,
            ISet <Guid> spotIdsUsed)
        {
            foreach (var smoothPass in smoothPasses)
            {
                switch (smoothPass)
                {
                case SmoothPassBooked specificSmoothPass:
                    ExecuteBookedPass(
                        specificSmoothPass,
                        breaksForThePeriodBeingSmoothed,
                        spotIdsUsed,
                        smoothBatchOutput);

                    break;

                case SmoothPassDefault specificSmoothPass:
                    ExecuteDefaultPass(
                        specificSmoothPass,
                        breaksForThePeriodBeingSmoothed,
                        spotIdsUsed,
                        smoothPassResults);

                    break;

                case SmoothPassUnplaced specificSmoothPass:
                    ExecuteUnplacedPass(
                        specificSmoothPass,
                        breaksForThePeriodBeingSmoothed,
                        spotIdsUsed,
                        smoothBatchOutput,
                        progSpotsNotUsed,
                        smoothPassResults);

                    break;
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Try and place unplaced spots, spots where there
        /// wasn't sufficient time in any break.
        /// </summary>
        private void ExecuteUnplacedPass(
            SmoothPassUnplaced specificSmoothPass,
            IReadOnlyCollection <Break> breaksForThePeriodBeingSmoothed,
            ISet <Guid> spotIdsUsed,
            SmoothBatchOutput smoothBatchOutput,
            List <Spot> progSpotsNotUsed,
            List <SmoothPassResult> smoothPassResults)
        {
            progSpotsNotUsed.Clear();

            var unbookedSpots = GetUnbookedSpots(_programmeSpots, spotIdsUsed);

            if (!unbookedSpots.Any())
            {
                return;
            }

            progSpotsNotUsed.AddRange(unbookedSpots);

            SmoothPassResult result = _smoothPassUnplacedExecuter.Execute(
                specificSmoothPass,
                breaksForThePeriodBeingSmoothed,
                spotIdsUsed,
                progSpotsNotUsed,
                _spotInfos);

            smoothPassResults.Add(result);

            if (result.CountPlacedSpots == 0)
            {
                return;
            }

            smoothBatchOutput.SpotsSetAfterMovingOtherSpots += result.CountPlacedSpots;

            // Update list of unplaced spots so that we can calculate
            // break avail adjustment. We placed previously unplaced
            // spots after moving other spots about.
            unbookedSpots = GetUnbookedSpots(_programmeSpots, spotIdsUsed);

            progSpotsNotUsed.Clear();
            progSpotsNotUsed.AddRange(unbookedSpots);
예제 #4
0
        /// <summary>
        /// Smooth one programme.
        /// </summary>
        public SmoothProgramme Execute(
            SmoothBatchOutput smoothBatchOutput,
            IReadOnlyCollection <SmoothPass> smoothPasses,
            IReadOnlyDictionary <string, Break> breaksByExternalRef,
            IReadOnlyDictionary <string, SpotPlacement> previousSpotPlacementsByExternalRef,
            IReadOnlyCollection <Break> breaksForThePeriodBeingSmoothed,
            ISet <Guid> spotIdsUsed)
        {
            RaiseDebug(
                $"Smoothing programme ID [{Log(_smoothProgramme.Programme.Id)}] " +
                $"in sales area {_smoothProgramme.SalesAreaName}" +
                Ellipsis
                );

            int countSpotsUnplacedBefore = _programmeSpots.Count(s => !s.IsBooked());

            (
                _smoothProgramme.BreaksWithPreviousSpots,
                _smoothProgramme.BreaksWithoutPreviousSpots
            ) = InitialiseSmoothBreaksForThisProgramme(
                smoothBatchOutput,
                spotIdsUsed);

            // For the breaks in this programme, perform multiple passes from
            // highest to lowest. If no programme breaks then don't execute any
            // passes otherwise we'll generate 'Unplaced spot attempt'
            // (TypeID=1) Smooth failures which is wrong. We'll generate 'No
            // placing attempt' Smooth failures at the end.
            var progSpotsNotUsed  = new List <Spot>();
            var smoothPassResults = new List <SmoothPassResult>();

            // We only attempt to place spots if there are breaks
            if (_smoothProgramme.ProgrammeSmoothBreaks.Count > 0)
            {
                ExecuteSmoothPasses(
                    smoothPasses,
                    breaksForThePeriodBeingSmoothed,
                    smoothBatchOutput,
                    progSpotsNotUsed,
                    smoothPassResults,
                    spotIdsUsed);
            }

            // For all spots that we attempted to place above (if we make at
            // least one pass then we 'attempt' to place every spot) remove them
            // from the list so that our final list contains only those spots
            // that we didn't make any attempt to place (E.g. No breaks or programmes).
            if (smoothPassResults.Count > 0)
            {
                SpotPlacementService.MarkSpotsAsPlacementWasAttempted(_programmeSpots, _spotInfos);
            }

            _smoothProgramme.SmoothFailures.AddRange(
                _smoothFailuresFactory.CreateSmoothFailuresForUnplacedSpots(
                    _runId,
                    _smoothProgramme.SalesAreaName,
                    smoothPasses,
                    smoothPassResults,
                    progSpotsNotUsed,
                    breaksByExternalRef,
                    previousSpotPlacementsByExternalRef,
                    _threadSafeCollections.CampaignsByExternalRef,
                    _threadSafeCollections.ClashesByExternalRef,
                    _threadSafeCollections.ProductsByExternalRef
                    )
                );

            _smoothProgramme.SmoothFailures.AddRange(
                _smoothFailuresFactory.CreateSmoothFailuresForPlacedSpots(
                    _runId,
                    _smoothProgramme.SalesAreaName,
                    _smoothProgramme.ProgrammeSmoothBreaks,
                    breaksByExternalRef,
                    previousSpotPlacementsByExternalRef,
                    _threadSafeCollections.CampaignsByExternalRef,
                    _threadSafeCollections.ClashesByExternalRef,
                    _threadSafeCollections.ProductsByExternalRef
                    )
                );

            IReadOnlyCollection <Spot> spotsToBatchSave = RenumberSpotPositionInBreak(
                smoothBatchOutput,
                spotIdsUsed);

            _smoothProgramme.SpotsToBatchSave.AddRange(spotsToBatchSave);

            var recommendationsForPlacedSpots = _smoothRecommendationsFactory.CreateRecommendationsForPlacedSpots(
                _smoothProgramme.ProgrammeSmoothBreaks,
                _smoothProgramme.Programme,
                _smoothProgramme.SalesArea,
                _processorDateTime
                );

            AddRecommendationsForPlacedSpotsToSmoothProgramme(
                recommendationsForPlacedSpots);

            int countSpotsUnplacedAfter = _programmeSpots.Count(s => !s.IsBooked());

            _smoothDiagnostics.LogPlacedSmoothSpots(
                _smoothProgramme.Programme,
                _smoothProgramme.ProgrammeSmoothBreaks);

            _smoothDiagnostics.LogProgramme(
                _smoothProgramme.Programme.Id,
                _smoothProgramme.ProgrammeSmoothBreaks,
                countSpotsUnplacedBefore,
                countSpotsUnplacedAfter
                );

            _smoothDiagnostics.Flush();

            return(_smoothProgramme);
        }
예제 #5
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);
        }