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);
 }
        private static void CheckProductClashExposureCount(
            IReadOnlyDictionary <string, Clash> clashesByExternalRef,
            IClashExposureCountService clashExposureCountService,
            SmoothFailureMessagesForSpotsCollection result,
            IReadOnlyDictionary <string, int> spotChildClashCodeCount,
            Spot spot,
            SpotInfo spotInfo,
            IReadOnlyCollection <Spot> childProductClashSpots)
        {
            if (!clashesByExternalRef.TryGetValue(spotInfo.ProductClashCode, out Clash childClash))
            {
                return;
            }

            int exposureCount = clashExposureCountService.Calculate(
                childClash.Differences,
                (childClash.DefaultPeakExposureCount, childClash.DefaultOffPeakExposureCount),
                (spot.StartDateTime, spot.SalesArea)
                );

            if (exposureCount > 0 &&
                childProductClashSpots.Count + spotChildClashCodeCount[spotInfo.ProductClashCode] > exposureCount)
            {
                result.Add(spot.Uid, SmoothFailureMessages.T1_ProductClash);
            }
        }
예제 #3
0
        public SmoothDateRange(
            Guid runId,
            Guid firstScenarioId,
            DateTime processorDateTime,
            SalesArea salesArea,
            ISmoothConfiguration smoothConfiguration,
            ISmoothDiagnostics smoothDiagnostics,
            ImmutableSmoothData threadSafeCollections,
            IClashExposureCountService clashExposureCountService,
            SaveSmoothChanges saveSmoothChanges,
            IRepositoryFactory repositoryFactory,
            Action <string> raiseInfo,
            Action <string> raiseWarning,
            Action <string, Exception> raiseException)
        {
            RaiseInfo                  = raiseInfo;
            RaiseWarning               = raiseWarning;
            RaiseException             = raiseException;
            _threadSafeCollections     = threadSafeCollections;
            _clashExposureCountService = clashExposureCountService;
            _saveSmoothChanges         = saveSmoothChanges;
            _repositoryFactory         = repositoryFactory;
            _runId                        = runId;
            _firstScenarioId              = firstScenarioId;
            _processorDateTime            = processorDateTime;
            _salesArea                    = salesArea;
            _smoothConfiguration          = smoothConfiguration;
            _smoothDiagnostics            = smoothDiagnostics;
            _smoothFailuresFactory        = new SmoothFailuresFactory(_smoothConfiguration);
            _smoothRecommendationsFactory = new SmoothRecommendationsFactory(_smoothConfiguration);

            _smoothPasses = _smoothConfiguration.SortedSmoothPasses;
        }
예제 #4
0
 public SmoothEngine(
     IRepositoryFactory repositoryFactory,
     IClashExposureCountService clashExposureCountService,
     RootFolder smoothLogFileFolder)
 {
     _repositoryFactory         = repositoryFactory;
     _smoothLogFileFolder       = smoothLogFileFolder;
     _clashExposureCountService = clashExposureCountService;
 }
예제 #5
0
        public BestBreakEvaluator(
            ISmoothDiagnostics smoothDiagnostics,
            SmoothResources smoothResources,
            IClashExposureCountService clashExposureCountService)
        {
            _smoothDiagnostics = smoothDiagnostics;
            _smoothResources   = smoothResources;

            _bestBreakFactorService         = new BestBreakFactorService(clashExposureCountService);
            _bestBreakFactorForGroupService = new BestBreakFactorForGroupService(_bestBreakFactorService);
        }
예제 #6
0
 public FewestProductClashesBreakFactor(
     SmoothBreak smoothBreak,
     IReadOnlyCollection <Spot> spots,
     IReadOnlyDictionary <Guid, SpotInfo> spotInfos,
     IReadOnlyDictionary <string, Product> productsByExternalRef,
     IReadOnlyDictionary <string, Clash> clashesByExternalRef,
     IProductClashChecker productClashChecker,
     IClashExposureCountService effectiveClashExposureCount)
 {
     _smoothBreak                 = smoothBreak;
     _spots                       = spots;
     _spotInfos                   = spotInfos;
     _productsByExternalRef       = productsByExternalRef;
     _clashesByExternalRef        = clashesByExternalRef;
     _productClashChecker         = productClashChecker;
     _effectiveClashExposureCount = effectiveClashExposureCount;
 }
        private static void CheckProductClashRulesWhenClashLimitsAreAllowed(
            IReadOnlyDictionary <Guid, SpotInfo> spotInfos,
            IReadOnlyDictionary <string, Clash> clashesByExternalRef,
            SmoothResources smoothResources,
            IClashExposureCountService clashExposureCountService,
            SmoothFailureMessagesForSpotsCollection result,
            IReadOnlyDictionary <string, int> spotChildClashCodeCount,
            IReadOnlyDictionary <string, int> spotParentClashCodeCount,
            IReadOnlyCollection <Spot> spotsAlreadyInTheBreak,
            Spot spot,
            SpotInfo spotInfo,
            IReadOnlyCollection <Spot> childProductClashSpots)
        {
            if (childProductClashSpots.Count > 0 && !String.IsNullOrEmpty(spotInfo.ProductClashCode))
            {
                CheckProductClashExposureCount(
                    clashesByExternalRef,
                    clashExposureCountService,
                    result,
                    spotChildClashCodeCount,
                    spot,
                    spotInfo,
                    childProductClashSpots);
            }

            // Check parent clashes
            var parentProductClashSpots = smoothResources.ProductClashChecker
                                          .GetProductClashesForSingleSpot(
                spot,
                spotsAlreadyInTheBreak,
                spotInfos,
                ClashCodeLevel.Parent)
                                          .ToList();

            if (parentProductClashSpots.Count > 0 && !String.IsNullOrEmpty(spotInfo.ParentProductClashCode))
            {
                CheckProductParentClashExposure(
                    clashesByExternalRef,
                    clashExposureCountService,
                    result,
                    spotParentClashCodeCount,
                    spot,
                    spotInfo,
                    parentProductClashSpots);
            }
        }
예제 #8
0
        /// <summary>
        /// Calculates the factor score for product clashes at both child and
        /// parent level
        /// </summary>
        private double GetFactorScoreForProductClashes(
            SmoothBreak smoothBreak,
            IReadOnlyCollection <Spot> spots,
            IReadOnlyDictionary <Guid, SpotInfo> spotInfos,
            IReadOnlyDictionary <string, Product> productsByExternalRef,
            IReadOnlyDictionary <string, Clash> clashesByExternalRef,
            IProductClashChecker productClashChecker,
            IClashExposureCountService effectiveClashExposureCount)
        {
            var factorScoreCalculator = new ProductClashFactorScore(effectiveClashExposureCount);

            return(factorScoreCalculator.GetFactorScoreForProductClashes(
                       smoothBreak.SmoothSpots.Select(s => s.Spot).ToList(),
                       spots,
                       spotInfos,
                       productsByExternalRef,
                       clashesByExternalRef,
                       productClashChecker.GetProductClashesForSingleSpot
                       ));
        }
예제 #9
0
        public SmoothWorkerForSalesAreaDuringDateTimePeriod(
            IRepositoryFactory repositoryFactory,
            string smoothLogFileFolder,
            ImmutableSmoothData threadSafeCollections,
            IClashExposureCountService clashExposureCountService,
            Action <string> raiseInfo,
            Action <string> raiseWarning,
            Action <string, Exception> raiseException
            )
        {
            _smoothLogFileFolder       = smoothLogFileFolder;
            _clashExposureCountService = clashExposureCountService;
            _repositoryFactory         = repositoryFactory;
            _smoothConfiguration       = threadSafeCollections.SmoothConfigurationReader;
            _threadSafeCollections     = threadSafeCollections;

            RaiseInfo      = raiseInfo;
            RaiseWarning   = raiseWarning;
            RaiseException = raiseException;
        }
예제 #10
0
 public SmoothPassUnplacedExecuter(
     ISmoothDiagnostics smoothDiagnostics,
     SmoothResources smoothResources,
     SmoothProgramme smoothProgramme,
     SponsorshipRestrictionService sponsorshipRestrictionService,
     IReadOnlyCollection <Programme> allProgrammesForPeriodAndSalesArea,
     ISmoothConfiguration smoothConfiguration,
     IClashExposureCountService clashExposureCountService,
     IImmutableDictionary <string, Clash> clashesByExternalRef,
     Action <string, Exception> raiseException
     )
 {
     _smoothDiagnostics                  = smoothDiagnostics;
     _smoothConfiguration                = smoothConfiguration;
     _smoothResources                    = smoothResources;
     _smoothProgramme                    = smoothProgramme;
     _clashExposureCountService          = clashExposureCountService;
     _sponsorshipRestrictionService      = sponsorshipRestrictionService;
     _allProgrammesForPeriodAndSalesArea = allProgrammesForPeriodAndSalesArea;
     _clashesByExternalRef               = clashesByExternalRef;
     RaiseException = raiseException;
 }
        /// <summary>
        /// Evaluate whether spots can be added to break. We consider time
        /// remaining, break type, product clashes, campaign clashes, spot end
        /// time (some spots may required to be position in the first N mins of
        /// the programme), sponsor rules.
        /// </summary>
        public static SmoothFailureMessagesForSpotsCollection ValidateAddSpots(
            SmoothBreak theSmoothBreak,
            Programme programme,
            SalesArea salesArea,
            IReadOnlyCollection <Spot> spotsForBreak,
            IReadOnlyDictionary <Guid, SpotInfo> spotInfos,
            IReadOnlyList <SmoothBreak> progSmoothBreaks,
            ProductClashRules productClashRule,
            bool respectCampaignClash,
            bool respectSpotTime,
            bool respectRestrictions,
            bool respectClashExceptions,
            SpotPositionRules breakPositionRules,
            SpotPositionRules requestedPositionInBreakRules,
            IReadOnlyDictionary <string, Clash> clashesByExternalRef,
            bool canSplitMultipartSpotsOverBreaks,
            SmoothResources smoothResources,
            IReadOnlyCollection <Break> breaksBeingSmoothed,
            IReadOnlyCollection <Programme> scheduleProgrammes,
            IClashExposureCountService clashExposureCountService,
            SponsorshipRestrictionService sponsorshipRestrictionsService)
        {
            var result = new SmoothFailureMessagesForSpotsCollection(theSmoothBreak);

            foreach (Guid spotUid in spotsForBreak.Select(s => s.Uid))
            {
                result.InitialiseForSpot(spotUid);
            }

            if (!IsSufficientRemainingDurationToAddSpots(
                    theSmoothBreak.RemainingAvailability,
                    spotsForBreak))
            {
                foreach (Guid spotUid in spotsForBreak.Select(s => s.Uid))
                {
                    result.Add(
                        spotUid,
                        SmoothFailureMessages.T1_InsufficentRemainingDuration);
                }
            }

            var(spotChildClashCodeCount, spotParentClashCodeCount) =
                ClashCodesForSpotsCount(spotsForBreak, spotInfos);

            // Check basics of whether we can add this spot to the break,
            // correct break type, sufficient time remaining, product clashes,
            // campaign clashes
            var spotsAlreadyInTheBreak = theSmoothBreak.SmoothSpots
                                         .ConvertAll(s => s.Spot);

            var canAddSpotService = CanAddSpotService.Factory(theSmoothBreak);

            foreach (Spot spot in spotsForBreak)
            {
                ValidateWithSponsorshipRestrictions(
                    theSmoothBreak,
                    sponsorshipRestrictionsService,
                    result,
                    spotsAlreadyInTheBreak,
                    spot);

                Guid spotUid = spot.Uid;

                if (!canAddSpotService.CanAddSpotWithBreakType(spot.BreakType))
                {
                    result.Add(spotUid, SmoothFailureMessages.T1_InvalidBreakType);
                }

                if (respectSpotTime && !canAddSpotService.CanAddSpotWithTime(spot.StartDateTime, spot.EndDateTime))
                {
                    result.Add(spotUid, SmoothFailureMessages.T1_InvalidSpotTime);
                }

                var(spotHasClashExceptionIncludes, spotHasClashExceptionExcludes) =
                    HasClashExceptionIncludesAndExcludes(
                        theSmoothBreak,
                        respectClashExceptions,
                        smoothResources,
                        result,
                        spot,
                        spotUid);

                bool shouldCheckProductClashRules =
                    (
                        productClashRule == ProductClashRules.NoClashes ||
                        productClashRule == ProductClashRules.LimitOnExposureCount
                    ) &&
                    !spotHasClashExceptionIncludes &&
                    !spotHasClashExceptionExcludes;

                if (shouldCheckProductClashRules)
                {
                    var childProductClashSpots = smoothResources.ProductClashChecker
                                                 .GetProductClashesForSingleSpot(
                        spot,
                        spotsAlreadyInTheBreak,
                        spotInfos,
                        ClashCodeLevel.Child);

                    switch (productClashRule)
                    {
                    case ProductClashRules.NoClashes:
                        var output = CheckProductClashRulesWhenNoClashesAreAllowed(
                            childProductClashSpots);

                        if (output != SmoothFailureMessages.T0_NoFailure)
                        {
                            result.Add(spotUid, output);
                        }

                        break;

                    case ProductClashRules.LimitOnExposureCount:
                        CheckProductClashRulesWhenClashLimitsAreAllowed(
                            spotInfos,
                            clashesByExternalRef,
                            smoothResources,
                            clashExposureCountService,
                            result,
                            spotChildClashCodeCount,
                            spotParentClashCodeCount,
                            spotsAlreadyInTheBreak,
                            spot,
                            spotInfos[spotUid],
                            childProductClashSpots);

                        break;
                    }
                }

                if (respectCampaignClash &&
                    smoothResources.CampaignClashChecker
                    .GetCampaignClashesForNewSpots(
                        new List <Spot> {
                    spot
                },
                        spotsAlreadyInTheBreak)
                    .Count > 0)
                {
                    result.Add(spotUid, SmoothFailureMessages.T1_CampaignClash);
                }
            }

            var multipartSpots = spotsForBreak
                                 .Where(s => s.IsMultipartSpot)
                                 .ToList();

            var nonMultipartSpots = spotsForBreak
                                    .Except(multipartSpots)
                                    .ToList();

            if (nonMultipartSpots.Count > 0)
            {
                ValidateNonMultipartSpots(
                    progSmoothBreaks,
                    breakPositionRules,
                    requestedPositionInBreakRules,
                    canAddSpotService,
                    result,
                    nonMultipartSpots);
            }

            if (multipartSpots.Count > 0)
            {
                ValidateMultipartSpots(
                    progSmoothBreaks,
                    breakPositionRules,
                    requestedPositionInBreakRules,
                    canSplitMultipartSpotsOverBreaks,
                    canAddSpotService,
                    result,
                    spotsAlreadyInTheBreak,
                    multipartSpots);
            }

            if (respectRestrictions)
            {
                ValidateWithSpotRestrictions(
                    theSmoothBreak,
                    programme,
                    salesArea,
                    spotsForBreak,
                    smoothResources,
                    breaksBeingSmoothed,
                    scheduleProgrammes,
                    result);
            }

            IndicateIfMultipartTopTailSameBreakSpotsCannotBeAdded(
                result,
                multipartSpots);

            return(RemoveDuplicateFailureMessages(spotsForBreak, result));
        }
예제 #12
0
 public BestBreakFactorService(
     IClashExposureCountService clashExposureCount) =>
예제 #13
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);
        }
예제 #14
0
 public ProductClashFactorScore(IClashExposureCountService effectiveClashExposureCount)
 {
     _effectiveClashExposureCount = effectiveClashExposureCount;
 }