/// <summary> /// Attempt to place unplaced spots by moving other spots. These are /// spots that couldn't be placed during the main pass, should only be /// spots that couldn't be placed due to no break with sufficient /// remaining time. /// </summary> public SmoothPassResult Execute( SmoothPassUnplaced smoothPass, IReadOnlyCollection <Break> breaksBeingSmoothed, ISet <Guid> spotIdsUsed, IReadOnlyCollection <Spot> spots, IReadOnlyDictionary <Guid, SpotInfo> spotInfos) { var smoothPassResult = new SmoothPassResult(smoothPass.Sequence); if (MaximumOfTwoBreaksWithRemainingTime(_smoothProgramme.ProgrammeSmoothBreaks)) { return(smoothPassResult); } // Get all spots for pass, order by priority for processing var spotFilter = new SpotFilter() { Sponsored = null, Preemptable = null, MinPreemptLevel = null, // Any MaxPreemptLevel = null, // Any HasBreakRequest = null, // Any BreakRequests = null, HasPositionInBreakRequest = null, // Any PositionInBreakRequestsToExclude = null, // Any HasMultipartSpots = null, MultipartSpots = null, // Any HasProductClashCode = null, ProductClashCodesToExclude = null, ExternalCampaignRefsToExclude = _smoothConfiguration.ExternalCampaignRefsToExclude, HasSpotEndTime = null, MinSpotLength = null, MaxSpotLength = null, SpotIdsToExclude = spotIdsUsed }; // Order the spots by priority var spotsToPlace = GetSpots(spotFilter, spots, spotInfos); var spotsOrdered = _smoothConfiguration.SortSpotsToPlace( spotsToPlace, (_smoothProgramme.Programme.StartDateTime, _smoothProgramme.Programme.Duration) ); // Try and place spots foreach (var spot in spotsOrdered) { try { PlaceSpotsResult placeSpotsResult = PlaceSpot( smoothPass, spot, spotInfos, _smoothProgramme.ProgrammeSmoothBreaks, breaksBeingSmoothed, spotIdsUsed); smoothPassResult.PlaceSpotsResultList.Add(placeSpotsResult); } catch (Exception exception) { RaiseException($"Error trying to place unplaced spot {spot.ExternalSpotRef}", exception); } } return(smoothPassResult);
/// <summary> /// Gets spots matching filter criteria /// </summary> /// <param name="spotFilter"></param> /// <param name="spots"></param> /// <param name="spotInfos"></param> /// <returns></returns> protected IEnumerable <Spot> GetSpots( SpotFilter spotFilter, IReadOnlyCollection <Spot> spots, IReadOnlyDictionary <Guid, SpotInfo> spotInfos) { var spotsOutput = new List <Spot>(spots); // Remove specific spots to exclude _ = spotsOutput.RemoveAll(s => !s.ClientPicked); if (spotFilter.SpotIdsToExclude?.Count > 0) { _ = spotsOutput.RemoveAll(s => spotFilter.SpotIdsToExclude.Contains(s.Uid)); } // Remove spots that don't meet criteria if (spotFilter.MinPreemptLevel != null) { _ = spotsOutput.RemoveAll(s => s.Preemptlevel < spotFilter.MinPreemptLevel); } if (spotFilter.MaxPreemptLevel != null) { _ = spotsOutput.RemoveAll(s => s.Preemptlevel > spotFilter.MaxPreemptLevel); } if (spotFilter.Sponsored != null) { _ = spotsOutput.RemoveAll(s => s.Sponsored != spotFilter.Sponsored); } if (spotFilter.Preemptable != null) { _ = spotsOutput.RemoveAll(s => s.Preemptable != spotFilter.Preemptable); } if (spotFilter.HasBreakRequest != null) { _ = spotsOutput.RemoveAll(s => (spotFilter.HasBreakRequest == true && String.IsNullOrEmpty(s.BreakRequest)) || (spotFilter.HasBreakRequest == false && !String.IsNullOrEmpty(s.BreakRequest)) ); } if (spotFilter.BreakRequests?.Count > 0) { _ = spotsOutput.RemoveAll(s => (!String.IsNullOrEmpty(s.BreakRequest) && !spotFilter.BreakRequests.Contains(s.BreakRequest)) || (String.IsNullOrEmpty(s.BreakRequest) && !spotFilter.BreakRequests.Contains(null)) ); } if (spotFilter.HasPositionInBreakRequest != null) { _ = spotsOutput.RemoveAll(s => (spotFilter.HasPositionInBreakRequest == true && String.IsNullOrEmpty(s.RequestedPositioninBreak)) || (spotFilter.HasPositionInBreakRequest == false && !String.IsNullOrEmpty(s.RequestedPositioninBreak)) ); } if (spotFilter.PositionInBreakRequestsToExclude?.Any() == true) { _ = spotsOutput.RemoveAll(s => spotFilter.PositionInBreakRequestsToExclude.Contains(s.RequestedPositioninBreak)); } if (spotFilter.HasMultipartSpots != null) { _ = spotsOutput.RemoveAll(s => (spotFilter.HasMultipartSpots == true && !s.IsMultipartSpot) || (spotFilter.HasMultipartSpots == false && s.IsMultipartSpot) ); } if (spotFilter.MultipartSpots?.Count > 0) { _ = spotsOutput.RemoveAll(s => s.IsMultipartSpot && !spotFilter.MultipartSpots.Contains(s.MultipartSpot)); } if (spotFilter.ExternalCampaignRefsToExclude?.Count > 0) { _ = spotsOutput.RemoveAll(s => spotFilter.ExternalCampaignRefsToExclude.Contains(s.ExternalCampaignNumber)); } if (spotFilter.ProductClashCodesToExclude?.Count > 0) { _ = spotsOutput.RemoveAll(s => spotFilter.ProductClashCodesToExclude.Contains(spotInfos[s.Uid].ProductClashCode)); } if (spotFilter.HasProductClashCode != null) { var spotsToRemove = new List <Spot>(); foreach (var spot in spotsOutput) { SpotInfo spotInfo = spotInfos[spot.Uid]; if ((spotFilter.HasProductClashCode == true && String.IsNullOrEmpty(spotInfo.ProductClashCode)) || (spotFilter.HasProductClashCode == false && !String.IsNullOrEmpty(spotInfo.ProductClashCode)) ) { spotsToRemove.Add(spot); } } if (spotsToRemove.Count > 0) { _ = spotsOutput.RemoveAll(s => spotsToRemove.Contains(s)); } } if (spotFilter.MinSpotLength != null) { _ = spotsOutput.RemoveAll(s => s.SpotLength.ToTimeSpan() < spotFilter.MinSpotLength); } if (spotFilter.MaxSpotLength != null) { _ = spotsOutput.RemoveAll(s => s.SpotLength.ToTimeSpan() > spotFilter.MaxSpotLength); } if (spotFilter.ProductAdvertiserIdentifiersToExclude?.Count > 0) { _ = spotsOutput.RemoveAll(s => spotFilter.ProductAdvertiserIdentifiersToExclude.Contains(spotInfos[s.Uid].ProductAdvertiserIdentifier) ); } if (spotFilter.HasSpotEndTime != null) { if (spotFilter.HasSpotEndTime.Value) { _ = spotsOutput.RemoveAll(s => s.EndDateTime < s.StartDateTime); } else { _ = spotsOutput.RemoveAll(s => s.EndDateTime > s.StartDateTime); } } // Order will be set elsewhere return(spotsOutput); }