/// <summary> /// <para> /// Get SmoothScenario instances for sponsored spots. We try and return scenarios. /// </para> /// <para> /// Unfortunately we have some business rules regarding sponsor spots /// coded here. We should try and make it more data driven later on. The /// business rules are that the first sponsor spot is placed in break /// #1, the next in break #2. /// </para> /// </summary> private IReadOnlyCollection <SmoothScenario> GetSmoothScenariosForSponsoredSpots( IReadOnlyCollection <Spot> spotsForBreak, IReadOnlyList <SmoothBreak> progSmoothBreaks, IReadOnlyCollection <SmoothFailureMessagesForSpotsCollection> validateAddSpotsToBreakResults, int lastSmoothScenarioSequence, SpotPositionRules breakPositionRules, bool respectSpotTime) { if (progSmoothBreaks.Count == 0) { return(new List <SmoothScenario>()); } var sponsoredSpots = spotsForBreak .Where(s => s.Sponsored); if (!sponsoredSpots.Any()) { return(new List <SmoothScenario>()); } var sponsoredSpot = sponsoredSpots.First(); var validBreaksForSpotTime = progSmoothBreaks .Where(sb => { var canAddSpotService = CanAddSpotService.Factory(sb); return(canAddSpotService.CanAddSpotWithTime(sponsoredSpot.StartDateTime, sponsoredSpot.EndDateTime)); }) .OrderBy(sb => sb.TheBreak.ScheduledDate); if (!validBreaksForSpotTime.Any()) { return(new List <SmoothScenario>()); } bool isRestrictedSpotTime = validBreaksForSpotTime.First().Position != 1 || validBreaksForSpotTime.Last().Position != progSmoothBreaks[progSmoothBreaks.Count - 1].Position; var smoothScenarios = new List <SmoothScenario>(); var listOfSpotValidations = validateAddSpotsToBreakResults.ToList(); var listOfProgrammeSmoothBreaks = progSmoothBreaks.ToList(); // Check each break, see if there are sponsored spots that we could move foreach (var progSmoothBreak in progSmoothBreaks) { // Lists of groups of spots moved out of break, used for // preventing duplicate scenarios which is inefficient var externalSpotRefsMovedOutOfBreakByGroup = new HashSet <string>(); // Get results for adding spot to break in its current state var validateAddSpotsToBreakResult = listOfSpotValidations[listOfProgrammeSmoothBreaks.IndexOf(progSmoothBreak)]; // Check if any failures. Ignore if T1_BreakPosition which // happens if break requested and this break is not allowed If // break position not allowed then don't move any spots. This // would happen if a break request was specified and this isn't // the requested break if (validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures.Count > 0 && // Failures prevent spot being added !validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures.Any(f => f.FailureMessage == SmoothFailureMessages.T1_BreakPosition)) { var spotGroupsToMove = new List <Spot[]>(); // Only consider moving spots from this break if any of the // following conditions are true: // - No break request and first or last break (as per // business rules). // - Requested break and this is requested break. // - Spot has restricted times and break is within it. bool addScenariosToFixFailures = false; if (!HasBreakRequest(sponsoredSpot) && ( progSmoothBreak.Position == listOfProgrammeSmoothBreaks[0].Position || progSmoothBreak.Position == progSmoothBreaks[progSmoothBreaks.Count - 1].Position ) ) { addScenariosToFixFailures = true; } else if (HasBreakRequest(sponsoredSpot)) { addScenariosToFixFailures = SpotUtilities.CanSpotBePlacedInRequestedBreakOrContainer( sponsoredSpot, progSmoothBreaks, breakPositionRules, respectSpotTime, validBreaksForSpotTime, isRestrictedSpotTime, progSmoothBreak); } else if (SpotUtilities.IsBreakWithinSpotTimeRestriction( respectSpotTime, isRestrictedSpotTime, validBreaksForSpotTime, progSmoothBreak)) { addScenariosToFixFailures = true; } if (addScenariosToFixFailures) { // See if we can find spots to move out of the break, // check failures foreach (var failure in validateAddSpotsToBreakResult[sponsoredSpot.Uid].Failures) { switch (failure.FailureMessage) { case SmoothFailureMessages.T1_CampaignClash: MoveCampaignClashSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove); break; case SmoothFailureMessages.T1_InsufficentRemainingDuration: MoveSpotsToIncreaseBreakAvailability(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove); break; case SmoothFailureMessages.T1_ProductClash: MoveProductClashSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove); break; case SmoothFailureMessages.T1_RequestedPositionInBreak: MoveRequestedPositionInBreakSpots(spotsForBreak, sponsoredSpot, progSmoothBreak, spotGroupsToMove); break; } } } // Add scenario to move group of spots, avoid duplicating // scenarios for same spot foreach (var spotGroupToMove in spotGroupsToMove) { string externalSpotRefsForGroup = SpotUtilities.GetListOfSpotExternalReferences( ",", spotGroupToMove.ToList()); if (!externalSpotRefsMovedOutOfBreakByGroup.Contains(externalSpotRefsForGroup)) { const int PriorityBase2 = 1_000_000; var smoothScenario = new SmoothScenario() { Id = Guid.NewGuid(), Sequence = lastSmoothScenarioSequence + 1, Priority = PriorityBase2 + (100_000 - spotGroupToMove[0].Preemptlevel) }; smoothScenario.Actions.Add( new SmoothActionMoveSpotToUnplaced( sequence: 1, spotGroupToMove.Select(s => s.ExternalSpotRef) ) ); smoothScenarios.Add(smoothScenario); lastSmoothScenarioSequence = smoothScenarios.Last().Sequence; _ = externalSpotRefsMovedOutOfBreakByGroup.Add(externalSpotRefsForGroup); } } } } return(smoothScenarios);
public void LogBestBreakFactorMessage( SmoothPass smoothPass, SmoothPassDefaultIteration smoothPassIteration, BestBreakFactorGroup bestBreakFactorGroup, Break theBreak, IReadOnlyCollection <Spot> spots, decimal?bestBreakScore, string message) { if (!_isloggingEnabled || !_logBestBreakFactor) { return; } string file = _logFilenameFactory.FilenameFor(LogFilenameFactory.LogFileType.BestBreak); bool addHeaders = !File.Exists(file); int attempts = 0; do { attempts++; try { using var writer = new StreamWriter(file, true); if (addHeaders) { writer.WriteLine(string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}{6}{0}{7}{0}{8}{0}{9}{0}{10}", Delimiter, "ProcessorDateTime", "PassSeq", "PassIterationSeq", "PlaceSpotsSeq", "GroupName", "GroupSeq", "ExternalBreakRef", "Spots", "BestBreakScore", "Message")); } writer.WriteLine(string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}{6}{0}{7}{0}{8}{0}{9}{0}{10}", Delimiter, _processorDateTimeToString, smoothPass == null ? "" : smoothPass.Sequence.ToString(), smoothPassIteration == null ? "" : smoothPassIteration.Sequence.ToString(), 0, bestBreakFactorGroup == null ? "" : bestBreakFactorGroup.Name, bestBreakFactorGroup == null ? "" : bestBreakFactorGroup.Sequence.ToString(), theBreak == null ? "" : theBreak.ExternalBreakRef, spots == null || spots.Count == 0 ? "" : SpotUtilities.GetListOfSpotExternalReferences(",", spots), bestBreakScore == null ? "" : bestBreakScore.Value.ToString("0.000000000000000000000000000000"), message)); writer.Flush(); writer.Close(); return; } catch (Exception exception) when(IsExceptionForFileInUse(exception) && attempts < 10) { Thread.Sleep(100); } } while (attempts != -1); }