public static string GetIES(this KAction action) { string label = null; // Amélioration if (action.Reduced != null) { // Amélioration I/E/S if (ActionsTimingsMoveManagement.IsActionInternal(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_Internal"); } else if (ActionsTimingsMoveManagement.IsActionExternal(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_External"); } else if (ActionsTimingsMoveManagement.IsActionDeleted(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_Deleted"); } else { throw new ArgumentOutOfRangeException(); } } return(label); }
/// <summary> /// Calcule les temps critiques. /// </summary> /// <param name="actions">Les actions.</param> /// <returns>Les temps critiques.</returns> protected override CriticalAction[] CalculateCriticalTimes(IEnumerable <KAction> actions) { var actionTimings = ActionsTimingsMoveManagement.CreateActionTimings(actions, this.UseManagedPredecessorsSuccessors, GetBuildStart, GetBuildFinish); return(ActionsTimingsMoveManagement.CalculateCriticalTimes(actionTimings)); }
/// <summary> /// Crée un nouvel élément représentant une action. /// </summary> /// <param name="action">L'action.</param> /// <returns>L'élément créé.</returns> protected override ActionGanttItem CreateNewActionItemImpl(KAction action) { ActionGanttItem item; if (this.IsViewByReferential()) { item = new ActionGanttItem(action, WBSHelper.IndentationFromWBS(action.WBS) + 1); } else { item = new ActionGanttItem(action, WBSHelper.IndentationFromWBS(action.WBS)); } item.CanResize = base.AllowTimingsDurationChange; Brush fillBrush, strokeBrush; GetActionBrushes(action, out fillBrush, out strokeBrush); item.FillBrush = fillBrush; item.StrokeBrush = strokeBrush; item.OrangeHeaderVisibility = ActionsTimingsMoveManagement.IsActionExternal(action) ? Visibility.Visible : Visibility.Collapsed; item.OrangeHeaderToolTip = LocalizationManager.GetString("VM_ActionManager_ExternalToolTip"); item.GreenHeaderToolTip = LocalizationManager.GetString("VM_ActionManager_NewToolTip"); return(item); }
/// <summary> /// Appelé lorsque un type de réduction (IES) a été appliqué sur une action. /// </summary> /// <param name="action">L'action.</param> protected override void OnReducedTypeApplied(KAction action) { var item = this.ItemsOfTypeAction.FirstOrDefault(i => i.Action == action); if (item != null) { item.OrangeHeaderVisibility = ActionsTimingsMoveManagement.IsActionExternal(action) ? Visibility.Visible : Visibility.Collapsed; } }
private void FixupImportedActions() { var sortedActions = _targetScenario.Actions.OrderBy(a => WBSHelper.GetParts(a.WBS), new WBSHelper.WBSComparer()).ToArray(); ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings(sortedActions, false); ActionsTimingsMoveManagement.UpdateVideoGroupsTiming(sortedActions); ActionsTimingsMoveManagement.UpdateBuildGroupsTiming(sortedActions); _targetScenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(_targetScenario); ActionsTimingsMoveManagement.DebugCheckAllWBS(_targetScenario); }
/// <summary> /// Appelé lorsque l'état de l'entité courante (spécifiée via RegisterToStateChanged) a changé. /// </summary> /// <param name="newState">le nouvel état.</param> protected override void OnEntityStateChanged(ObjectState newState) { CanChange &= newState == ObjectState.Unchanged; if (CurrentActionItem is ActionGanttItem actionGanttItem) { BrushesHelper.GetBrush(actionGanttItem.Action.Category?.Color, true, out Brush fillBrush, out Brush strokeBrush); actionGanttItem.FillBrush = fillBrush; actionGanttItem.StrokeBrush = strokeBrush; actionGanttItem.OrangeHeaderVisibility = ActionsTimingsMoveManagement.IsActionExternal(actionGanttItem.Action) ? Visibility.Visible : Visibility.Collapsed; ActionsManager.UpdateReductionDescription(actionGanttItem.Action, true); } }
public void TestCanAddPredecessor() { var t1 = new KAction() { Label = "T1", WBS = "1" }; var g1 = new KAction() { Label = "G1", WBS = "2" }; var t2 = new KAction() { Label = "T2", WBS = "2.1" }; var t3 = new KAction() { Label = "T3", WBS = "2.2" }; var t4 = new KAction() { Label = "T4", WBS = "3" }; var t5 = new KAction() { Label = "T5", WBS = "4" }; var actions = new List <KAction>() { t1, g1, t2, t3, t4, t5 }; t3.Predecessors.Add(t1); t3.Predecessors.Add(t2); t4.Predecessors.Add(t3); t5.Predecessors.Add(t3); var collection = new BulkObservableCollection <DataTreeGridItem>(); var manager = new GridActionsManager(collection, null, null); manager.ChangeView(GanttGridView.WBS, null); manager.RegisterInitialActions(actions); Assert.IsFalse(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), t1, t1)); Assert.IsFalse(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), t1, t3)); Assert.IsFalse(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), g1, t3)); Assert.IsTrue(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), t4, t1)); Assert.IsTrue(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), t5, t1)); Assert.IsTrue(ActionsTimingsMoveManagement.CheckCanAddPredecessor(manager.GetActionsSortedByWBS(), t2, t1)); }
public void Delete_Random_Consistency() { var random = new Random(); var testEnd = DateTime.Now.AddSeconds(30); while (DateTime.Now < testEnd) { var tasks = new List <KAction>(); var rootTasksCount = random.Next(10); var currentWbsParts = new Stack <int>(); currentWbsParts.Push(0); PopulateRandom(random, tasks, currentWbsParts, 5, false); WriteTasksWBS(tasks); ActionsTimingsMoveManagement.DebugCheckAllWBS(tasks); if (CheckWBS(tasks) != null) { throw new InvalidOperationException("Wrong generation"); } while (tasks.Count > 0) { var index = random.Next(tasks.Count); var countToDelete = Math.Min(random.Next(1, 10), tasks.Count); var toDelete = tasks[index]; var currentStatus = Clone(tasks).ToArray(); ActionsTimingsMoveManagement.DeleteUpdateWBS(tasks.ToArray(), toDelete); tasks.Remove(toDelete); TestContext.WriteLine("Deleted: {0} {1}.", toDelete.WBS, toDelete.Label); var wrongWbs = CheckWBS(tasks); if (wrongWbs != null) { // Rappeler l'état d'origine et l'élément supprimé TestContext.WriteLine("Fail!"); TestContext.WriteLine("--- Before deletion, indented format:"); WriteTasksWBS(currentStatus); TestContext.WriteLine("--- Before deletion, appended format: {0}", string.Join(" ", currentStatus.Select(t => t.WBS))); TestContext.WriteLine("--- Deleted element {0} {1}", toDelete.WBS, toDelete.Label); TestContext.WriteLine("--- After deletion, indented format:"); WriteTasksWBS(tasks); Assert.Fail("The WBS {0} is invalid", wrongWbs); } ActionsTimingsMoveManagement.DebugCheckAllWBS(tasks); } } }
/// <summary> /// Met à jour le début, la durée et la fin sur les éléments de groupe /// </summary> protected override void UpdateBuildGroupsTiming() { if (!EnableGroupsTimingCoercion || (!AllowTimingsChange && !_useGanttItemsTimingsOnly) || _isUpdatingGroupsTimings) { return; } _isUpdatingGroupsTimings = true; ActionsTimingsMoveManagement.UpdateBuildGroupsTiming(GetActionsSortedByWBS(), GetBuildStart, GetBuildFinish, SetBuildStart, SetBuildFinish); _isUpdatingGroupsTimings = false; }
/// <summary> /// Détermine si un lien peut être ajouté. /// </summary> /// <param name="item">L'élément.</param> /// <param name="predecessor">Le prédécesseur.</param> /// <returns><c>true</c> si le lien peut être ajouté.</returns> public bool DependencyCreationValidatorProvider(GanttChartItem item, GanttChartItem predecessor) { if (!DefaultDependencyCreationValidatorProvider(item, predecessor)) { return(false); } var actionItem = item as ActionGanttItem; var predecessorItem = predecessor as ActionGanttItem; if (actionItem == null || predecessorItem == null) { return(false); } return(ActionsTimingsMoveManagement.CheckCanAddPredecessor(base.GetActionsSortedByWBS(), actionItem.Action, predecessorItem.Action)); }
/// <summary> /// Corrige les temps en fonction des prédécesseurs et successeurs de chaque action. /// </summary> public override void FixPredecessorsSuccessorsTimings() { if (!EnablePredecessorTimingFix || (!AllowTimingsChange && !_useGanttItemsTimingsOnly) || IsFixingPredecessorsSuccessorsTiming) { return; } IsFixingPredecessorsSuccessorsTiming = true; ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings( GetActionsSortedByWBS(), this.UseManagedPredecessorsSuccessors, this.GetBuildStart, this.GetBuildFinish, this.SetBuildStart, this.SetBuildFinish); IsFixingPredecessorsSuccessorsTiming = false; this.UpdateBuildGroupsTiming(); this.UpdateCriticalPath(); }
public void FixPredecessorsSuccessorsTimings_Outside_Of_CriticalPath() { // Création de 3 tâches successives // Voilà une représentation des tâches // -- T1 // -- T2 // -- T3 // - T4 // Et les précédesseurs : // T3 -> T2 -> T1, // T3 -> T4 -> T1 // Le test va consister à supprimer T2 // Quand on le supprime, T3 devrait se décaller de 1 vers la droite que correspondre au précédesseur T4 var t1 = CreateAction("1", 2); var t2 = CreateAction("2", 2, t1); var t4 = CreateAction("4", 1, t1); var t3 = CreateAction("3", 2, t2, t4); var actions = new KAction[] { t1, t2, t3, t4 }; ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings(actions, false); // On vérifie que le temps process a bien été modifié Assert.AreEqual(4, t3.BuildStart); Assert.AreEqual(6, t3.BuildFinish); // On supprime t2 t3.Predecessors.Remove(t2); actions = new KAction[] { t1, t3, t4 }; ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings(actions, false); // On vérifie que le temps process a bien été modifié Assert.AreEqual(3, t3.BuildStart); Assert.AreEqual(5, t3.BuildFinish); }
private void ImportActions() { // Trier les actions par WBS _actions = _import.ExportedVideoDecomposition.Actions.OrderBy(a => WBSHelper.GetParts(a.WBS), new WBSHelper.WBSComparer()).ToArray(); // Actions var parents = new Dictionary <KAction, KAction>(); // Faire une liste des parents foreach (var action in _actions) { var parent = WBSHelper.GetParent(action, _actions); if (parent != null) { parents[action] = parent; } } foreach (var action in _actions) { action.ActionId = default(int); action.OriginalActionId = null; SharedScenarioActionsOperations.ApplyNewReduced(action, KnownActionCategoryTypes.I); // Réinitialiser les timings // Ils seront ensuite automatiquement recalculés en fonction des pred/succ action.BuildStart = 0; action.BuildDuration = action.Duration; var parent = parents.ContainsKey(action) ? parents[action] : null; ActionsTimingsMoveManagement.InsertUpdateWBS(_targetScenario.Actions.ToArray(), action, parent, -1); _targetScenario.Actions.Add(action); } }
/// <summary> /// Sauvegarde le scénario spécifié. /// </summary> /// <param name="context">Le contexte EF.</param> /// <param name="allScenarios">Tous les scénarios liés.</param> /// <param name="recursive"><c>true</c> pour appliquer les changements récursivement sur les scénarios dérivés.</param> public static Task SaveBuildScenario(KsmedEntities context, Scenario[] allScenarios, Scenario updatedScenario, bool recursive) { // Consolider les solutions string[] distinctSolutionsLabels = updatedScenario.Actions .Where(a => a.IsReduced && !string.IsNullOrWhiteSpace(a.Reduced.Solution)) .Select(a => a.Reduced.Solution) .Distinct() .ToArray(); // Ajouter les nouvelles solutions foreach (string solutionLabel in distinctSolutionsLabels) { if (!updatedScenario.Solutions.Any(s => s.SolutionDescription == solutionLabel)) { // Créer une nouvelle solution Solution solution = new Solution() { SolutionDescription = solutionLabel, }; updatedScenario.Solutions.Add(solution); } } EnsureEmptySolutionExists(updatedScenario); // Supprimer les anciennes solutions Solution[] allSolutions = updatedScenario.Solutions.Where(s => !s.IsEmpty).ToArray(); foreach (Solution sol in allSolutions) { if (!distinctSolutionsLabels.Contains(sol.SolutionDescription)) { sol.MarkAsDeleted(); updatedScenario.Solutions.Remove(sol); } } // Copier le temps original foreach (KActionReduced reduced in updatedScenario.Actions .Where(a => a.IsReduced) .Select(a => a.Reduced) .Where(r => r.OriginalBuildDuration == default(long))) { reduced.OriginalBuildDuration = reduced.Action.BuildDuration; } // Appliquer l'état Approved UdpateSolutionsApprovedState(updatedScenario); ActionsRecursiveUpdate.UpdateActions(context, updatedScenario, allScenarios, out KAction[] actionsToDelete, out IList <KAction> actionsWithOriginal); KAction[] allActions = allScenarios .SelectMany(s => s.Actions) .Where(a => a.IsNotMarkedAsUnchanged || (a.IsReduced && a.Reduced.IsNotMarkedAsUnchanged)) .ToArray(); foreach (KAction action in allActions) { context.KActions.ApplyChanges(action); if (action.IsReduced) { context.KActionsReduced.ApplyChanges(action.Reduced); } } foreach (KAction action in actionsWithOriginal) { SetActionsOriginalReference(context, action); } foreach (Scenario scenario in allScenarios.Where(s => s.IsNotMarkedAsUnchanged)) { context.Scenarios.ApplyChanges(scenario); } foreach (Solution solution in updatedScenario.Solutions) { context.Solutions.ApplyChanges(solution); } // Vérifier que tout est correct ActionsTimingsMoveManagement.DebugCheckAllWBS(allScenarios.Where(s => s.IsNotMarkedAsUnchanged)); return(context.SaveChangesAsync()); }
/// <summary> /// Sauvegarde les actions spécifiées. /// </summary> /// <param name="context">Le contexte.</param> /// <param name="allScenarios">Tous les scénarios liés.</param> /// <param name="updatedScenario">Le scénario qui a été mis à jour.</param> /// <param name="recursive"><c>true</c> pour appliquer les changements récursivement sur les scénarios dérivés.</param> public static Task SaveAcquireData(KsmedEntities context, Scenario[] allScenarios, Scenario updatedScenario, bool recursive) { KAction[] actionsToDelete; IList <KAction> actionsWithOriginal; if (recursive) { ActionsRecursiveUpdate.UpdateActions(context, updatedScenario, allScenarios, out actionsToDelete, out actionsWithOriginal); } else { actionsWithOriginal = null; actionsToDelete = updatedScenario.Actions.Where(a => a.IsMarkedAsDeleted).ToArray(); updatedScenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(updatedScenario); } // Consolider les solutions vides EnsureEmptySolutionExists(updatedScenario); UdpateSolutionsApprovedState(updatedScenario); KAction[] allActions = allScenarios .SelectMany(s => s.Actions) .Where(a => a.IsNotMarkedAsUnchanged) .ToArray(); foreach (KAction action in allActions) { if (!action.IsReduced) { ApplyNewReduced(action, KnownActionCategoryTypes.I); } context.KActions.ApplyChanges(action); context.KActionsReduced.ApplyChanges(action.Reduced); } if (actionsWithOriginal != null) { foreach (KAction action in actionsWithOriginal) { SetActionsOriginalReference(context, action); } } foreach (Scenario scenario in allScenarios.Where(s => s.IsNotMarkedAsUnchanged)) { context.Scenarios.ApplyChanges(scenario); } context.Scenarios.ApplyChanges(updatedScenario); foreach (KAction action in actionsToDelete) { action.Predecessors.Clear(); action.Successors.Clear(); action.MarkAsDeleted(); // Ne pas appeler ApplyChanges car les self tracking le gèrent mal (plantage lors de la sauvegarde) // Ajouter l'action au contexte si elle n'y est pas attachée if (!context.ObjectStateManager.TryGetObjectStateEntry(action, out ObjectStateEntry entry)) { context.AddObject(KsmedEntities.KActionsEntitySetName, action); context.ObjectStateManager.ChangeObjectState(action, EntityState.Deleted); } else { context.KActions.DeleteObject(action); } } // Vérifier que tout est correct if (recursive) { ActionsTimingsMoveManagement.DebugCheckAllWBS(allScenarios.Where(s => s.IsNotMarkedAsUnchanged)); } return(context.SaveChangesAsync()); }
/// <summary> /// Sauvegarde les actions spécifiées. /// </summary> /// <param name="context">Le contexte.</param> /// <param name="allScenarios">Tous les scénarios liés.</param> /// <param name="updatedScenario">Le scénario qui a été mis à jour.</param> /// <param name="recursive"><c>true</c> pour appliquer les changements récursivement sur les scénarios dérivés.</param> public async Task <Scenario> SaveAcquireData(KsmedEntities context, Scenario[] allScenarios, Scenario updatedScenario, bool recursive) { try { // Don't insert a thumbnail that already exists var actionsWithNewThumbnail = updatedScenario.Actions.Where(_ => (_.IsMarkedAsAdded && _.Thumbnail != null) || (_.IsMarkedAsModified && _.ChangeTracker.ModifiedValues.ContainsKey(nameof(KAction.Thumbnail)) && _.Thumbnail != null)); foreach (var action in actionsWithNewThumbnail) { CloudFile thumbnail = updatedScenario.Actions.Where(_ => _.Thumbnail != null && _.ActionId != action.ActionId).Select(_ => _.Thumbnail).FirstOrDefault(_ => _.Hash == action.ThumbnailHash); if (thumbnail == null) { thumbnail = await context.CloudFiles.SingleOrDefaultAsync(_ => _.Hash == action.Thumbnail.Hash); } if (thumbnail != null) { action.Thumbnail = thumbnail; } } KAction[] actionsToDelete; IList <KAction> actionsWithOriginal; if (recursive) { ActionsRecursiveUpdate.UpdateActions(context, updatedScenario, allScenarios, out actionsToDelete, out actionsWithOriginal); } else { actionsWithOriginal = null; actionsToDelete = updatedScenario.Actions.Where(a => a.IsMarkedAsDeleted).ToArray(); updatedScenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(updatedScenario); } // Consolider les solutions vides EnsureEmptySolutionExists(updatedScenario); UdpateSolutionsApprovedState(updatedScenario); // Update InheritedAction if original will be deleted var actionIdsToDelete = actionsToDelete.Select(_ => _.ActionId).ToList(); var inheritedActions = await context.KActions .Where(_ => _.OriginalActionId != null && actionIdsToDelete.Contains(_.OriginalActionId.Value)) .ToListAsync(); foreach (var inheritedAction in inheritedActions) { inheritedAction.OriginalActionId = null; context.KActions.ApplyChanges(inheritedAction); } KAction[] allActions = allScenarios .SelectMany(s => s.Actions) .Where(a => a.IsNotMarkedAsUnchanged) .ToArray(); foreach (KAction action in allActions) { if (!action.IsReduced) { ApplyNewReduced(action, KnownActionCategoryTypes.I); } context.KActions.ApplyChanges(action); context.KActionsReduced.ApplyChanges(action.Reduced); } if (actionsWithOriginal != null) { foreach (KAction action in actionsWithOriginal) { SetActionsOriginalReference(context, action); } } foreach (Scenario scenario in allScenarios.Where(s => s.IsNotMarkedAsUnchanged)) { context.Scenarios.ApplyChanges(scenario); } context.Scenarios.ApplyChanges(updatedScenario); foreach (KAction action in actionsToDelete) { action.Predecessors.Clear(); action.Successors.Clear(); action.MarkAsDeleted(); // Ne pas appeler ApplyChanges car les self tracking le gèrent mal (plantage lors de la sauvegarde) // Ajouter l'action au contexte si elle n'y est pas attachée if (!context.ObjectStateManager.TryGetObjectStateEntry(action, out ObjectStateEntry entry)) { context.AddObject(KsmedEntities.KActionsEntitySetName, action); context.ObjectStateManager.ChangeObjectState(action, EntityState.Deleted); } else { context.KActions.DeleteObject(action); } } // Vérifier que tout est correct if (recursive) { ActionsTimingsMoveManagement.DebugCheckAllWBS(allScenarios.Where(s => s.IsNotMarkedAsUnchanged)); } await context.SaveChangesAsync(); return(updatedScenario); } catch (Exception ex) { TraceManager.TraceError(ex, $"Error while saving scenario {updatedScenario.ScenarioId}"); throw; } }
/// <summary> /// Crée un scénario cible à partir d'un autre scénario, initial ou cible. /// </summary> /// <param name="context">Le contexte.</param> /// <param name="sourceScenario">Le scenario source.</param> /// <param name="natureCode">Le code de la nature.</param> /// <param name="save"><c>true</c> pour sauvegarder le scénario créé.</param> /// <param name="targetNumber">Le numéro cible.</param> /// <returns> /// Le scénario créé /// </returns> public static async Task <Scenario> CreateDerivatedScenario(KsmedEntities context, Scenario sourceScenario, string natureCode, bool save, int targetNumber) { // Charger les données du scénario source var newScenario = new Scenario(); ActionCloneBehavior cloneBehavior; if (sourceScenario.NatureCode == KnownScenarioNatures.Initial && natureCode == KnownScenarioNatures.Target) { cloneBehavior = ActionCloneBehavior.InitialToTarget; } else if (sourceScenario.NatureCode == KnownScenarioNatures.Target && natureCode == KnownScenarioNatures.Target) { cloneBehavior = ActionCloneBehavior.TargetToTarget; } else if (sourceScenario.NatureCode == KnownScenarioNatures.Target && natureCode == KnownScenarioNatures.Realized) { cloneBehavior = ActionCloneBehavior.TargetToRealized; } else if (sourceScenario.NatureCode == KnownScenarioNatures.Realized && natureCode == KnownScenarioNatures.Initial) { cloneBehavior = ActionCloneBehavior.RealizedToNewInitial; } else if (sourceScenario.NatureCode == KnownScenarioNatures.Target && natureCode == KnownScenarioNatures.Initial) { cloneBehavior = ActionCloneBehavior.TargetToNewInitial; } else if (sourceScenario.NatureCode == KnownScenarioNatures.Initial && natureCode == KnownScenarioNatures.Initial) { cloneBehavior = ActionCloneBehavior.InitialToNewInitial; } else { throw new InvalidOperationException("Conversion impossible pour ces scénarios"); } switch (natureCode) { case KnownScenarioNatures.Target: newScenario.Label = LocalizationManager.GetString("Business_AnalyzeService_TargetScenarioLabel") + " " + targetNumber; break; case KnownScenarioNatures.Realized: newScenario.Label = LocalizationManager.GetString("Business_AnalyzeService_ValidationScenarioLabel"); break; case KnownScenarioNatures.Initial: newScenario.Label = LocalizationManager.GetString("Business_AnalyzeService_InitialScenarioLabel"); break; default: throw new ArgumentOutOfRangeException(nameof(natureCode)); } newScenario.StateCode = KnownScenarioStates.Draft; newScenario.NatureCode = natureCode; if (cloneBehavior != ActionCloneBehavior.RealizedToNewInitial && cloneBehavior != ActionCloneBehavior.TargetToNewInitial && cloneBehavior != ActionCloneBehavior.InitialToNewInitial) { newScenario.ProjectId = sourceScenario.ProjectId; newScenario.Original = sourceScenario; newScenario.OriginalScenarioId = sourceScenario.ScenarioId; } newScenario.IsShownInSummary = true; newScenario.CriticalPathIDuration = sourceScenario.CriticalPathIDuration; string[] scenarioLAbels = await EnsureCanShowScenarioInSummary(newScenario, true); // Copier toutes les actions foreach (var action in sourceScenario.Actions.ToArray()) { var newAction = CloneAction(action, cloneBehavior); newAction.OriginalActionId = action.ActionId; newAction.Original = action; if (newAction.Reduced != null) { newAction.Reduced.OriginalBuildDuration = action.BuildDuration; } // S'il s'agit d'un scénario validé, utiliser les temps process en tant que temps vidéo if (cloneBehavior == ActionCloneBehavior.TargetToRealized) { newAction.Start = newAction.BuildStart; newAction.Finish = newAction.BuildFinish; } newScenario.Actions.Add(newAction); } SharedScenarioActionsOperations.EnsureEmptySolutionExists(newScenario); SharedScenarioActionsOperations.UdpateSolutionsApprovedState(newScenario); // Copier les liens prédécesseurs successeurs foreach (var action in sourceScenario.Actions.ToArray()) { var newAction = newScenario.Actions.FirstOrDefault(a => a.OriginalActionId == action.ActionId); if (newAction != null) { foreach (var predecessor in action.Predecessors) { var newPredecessor = newScenario.Actions.FirstOrDefault(a => a.OriginalActionId == predecessor.ActionId); if (newPredecessor != null) { newAction.Predecessors.Add(newPredecessor); } } } } //Suppression des actions avec durée = 0 if (cloneBehavior == ActionCloneBehavior.TargetToRealized || cloneBehavior == ActionCloneBehavior.TargetToTarget) { ActionsRecursiveUpdate.RemoveEmptyDurationActionsAndGroupsFromNewScenario(newScenario); } if (cloneBehavior != ActionCloneBehavior.TargetToRealized && // ToDelete ne s'applique pas aux scenarios validés cloneBehavior != ActionCloneBehavior.InitialToNewInitial && // ToDelete ne s'applique pas aux scenarios initiaux cloneBehavior != ActionCloneBehavior.TargetToNewInitial && // ToDelete ne s'applique pas aux scenarios initiaux cloneBehavior != ActionCloneBehavior.RealizedToNewInitial) // ToDelete ne s'applique pas aux scenarios initiaux { foreach (var newAction in newScenario.Actions) { // Si la category associée est dite "à supprimer", modifier la tache optimisée à "à supprimer" if (newAction.Category != null && newAction.Category.ActionTypeCode == KnownActionCategoryTypes.S) { SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.S); } } } if (save) { context.Scenarios.ApplyChanges(newScenario); await context.SaveChangesAsync(); } ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings(newScenario.Actions.ToArray(), false); ActionsTimingsMoveManagement.UpdateVideoGroupsTiming(newScenario.Actions.ToArray()); ActionsTimingsMoveManagement.UpdateBuildGroupsTiming(newScenario.Actions.ToArray()); newScenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(newScenario); // Supprimer les liens vers les originaux car dans un autre projet if (cloneBehavior == ActionCloneBehavior.RealizedToNewInitial || cloneBehavior == ActionCloneBehavior.TargetToNewInitial || cloneBehavior == ActionCloneBehavior.InitialToNewInitial) { foreach (var action in newScenario.Actions) { action.Original = null; action.OriginalActionId = null; } newScenario.Original = null; newScenario.OriginalScenarioId = null; } return(newScenario); }
/// <summary> /// Clone une action pour qu'elle soit utilisée dans un nouveau scénario. /// </summary> /// <param name="action">The action.</param> /// <param name="cloneBehavior">The clone behavior.</param> /// <returns></returns> public static KAction CloneAction(KAction action, ActionCloneBehavior cloneBehavior) { var newAction = new KAction(); var originalValues = action.GetCurrentValues(); // Ignorer certaines propriétés var excludedPropertyNames = new List <string> { "ActionId", "ScenarioId", "OriginalActionId", "Predecessors", "Successors", "Reduced" }; // Quand on passe vers un scénario de validation, il ne faut pas copier la vignette car on supprime les leins avec les vidéos if (cloneBehavior == ActionCloneBehavior.TargetToRealized) { excludedPropertyNames.Add("Thumbnail"); excludedPropertyNames.Add("IsThumbnailSpecific"); excludedPropertyNames.Add("ThumbnailPosition"); } // Vérifier que ces noms de propriétés soient corrects if (excludedPropertyNames.Except(originalValues.Keys).Any()) { throw new InvalidOperationException("Les noms de propriétés présents dans excludedPropertyNames ne sont pas valides."); } foreach (var kvp in originalValues) { if (!excludedPropertyNames.Contains(kvp.Key)) { newAction.SetPropertyValue(kvp.Key, kvp.Value); } } // Copier les liens actions / référentiel CloneReferentialActionsLinks(action, newAction); // Copier la partie Reduced switch (cloneBehavior) { case ActionCloneBehavior.InitialToTarget: { // S'il existe un prétypage par la catégorie, il est prioritaire. Sinon, utiliser celui sur l'action s'il y en a. string actionTypeCode; if (action.Category != null && action.Category.ActionTypeCode != null) { actionTypeCode = action.Category.ActionTypeCode; } else { actionTypeCode = action.IsReduced ? action.Reduced.ActionTypeCode : KnownActionCategoryTypes.I; } SharedScenarioActionsOperations.ApplyNewReduced(newAction, actionTypeCode); } break; case ActionCloneBehavior.TargetToTarget: if (action.Reduced.ActionTypeCode == null) { throw new InvalidOperationException("Une action cible doit toujours être réduite avec un type"); } if (!ActionsTimingsMoveManagement.GetIsSolutionApproved(action).GetValueOrDefault(true)) { // Tache I provenant du grand parent, on n'applique pas le prétype mais on applique la réduc SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.I); } else { switch (action.Reduced.ActionTypeCode) { case KnownActionCategoryTypes.I: // Déterminer si la tâche provient d'un scénario ancêtre if (action.OriginalActionId != null) { // Tache I provenant du grand parent, on n'applique pas le prétype mais on applique la réduc SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.I); } else { // Tache I provenant du parent, on applique le prétypage et la réduc s'il y en a string actionTypeCode; if (action.Category != null && action.Category.ActionTypeCode != null) { actionTypeCode = action.Category.ActionTypeCode; } else { actionTypeCode = KnownActionCategoryTypes.I; } SharedScenarioActionsOperations.ApplyNewReduced(newAction, actionTypeCode); } break; case KnownActionCategoryTypes.E: // Tâche E, on conserve E et on applique la réduc SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.E); break; case KnownActionCategoryTypes.S: SharedScenarioActionsOperations.ApplyReduced(action, newAction); break; default: throw new ArgumentOutOfRangeException(nameof(action), new ArgumentOutOfRangeException(nameof(KAction.Reduced), new ArgumentOutOfRangeException(nameof(KActionReduced.ActionTypeCode)))); } } break; case ActionCloneBehavior.TargetToRealized: // Pour les tâches externes, il faut les faire réapparaitre comme externes // Pour les tâches internes, il faut qu'elle aient une partie réduite pour pouvoir les repasser à exerne. switch (action.Reduced.ActionTypeCode) { case KnownActionCategoryTypes.I: SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.I); break; case KnownActionCategoryTypes.E: SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.E); break; } break; case ActionCloneBehavior.Cascade: SharedScenarioActionsOperations.ApplyReduced(action, newAction); break; case ActionCloneBehavior.RealizedToNewInitial: case ActionCloneBehavior.TargetToNewInitial: case ActionCloneBehavior.InitialToNewInitial: // Pour les tâches externes, il faut les faire réapparaitre comme externes // Pour les tâches internes, il faut qu'elle aient une partie réduite pour pouvoir les repasser à exerne. if (action.Reduced != null) { switch (action.Reduced.ActionTypeCode) { case KnownActionCategoryTypes.I: SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.I); break; case KnownActionCategoryTypes.E: SharedScenarioActionsOperations.ApplyNewReduced(newAction, KnownActionCategoryTypes.E); break; } } break; default: throw new ArgumentOutOfRangeException(nameof(cloneBehavior)); } return(newAction); }
/// <summary> /// Calcule les données pour un côté. /// </summary> /// <param name="actionFilter">Le filtre sur les actions.</param> /// <param name="distinctReferentials">The distinct referentials.</param> /// <returns> /// Les données créées. /// </returns> private IEnumerable <TData> GenerateItems(Func <KAction, bool> actionFilter, TReferential[] distinctReferentials, out TData[] total) { var scenarios = ParentViewModel.ScenariosToShow; var dataCol = new List <TData>(); foreach (var sc in scenarios) { var data = new TData() { Scenario = sc.Label }; var dataItems = new List <TDataItem>(); foreach (var referential in distinctReferentials) { var actions = sc.Actions. Where(actionFilter) .Where(a => MatchesReferential(a, referential) && !a.IsGroup); var actionsNotDeletedCount = actions.Where(a => !ActionsTimingsMoveManagement.IsActionDeleted(a)).Count(); var item = new TDataItem() { ReferentialName = referential != null ? referential.Label : LocalizationManager.GetString("ViewModel_Restitution_EmptyReferential"), ReferentialDuration = actions.Any() ? actions.Sum(a => a.BuildDuration) : 0, ReferentialOccurrences = actionsNotDeletedCount, ValueMode = SelectedValueMode, IsStandard = referential is IActionReferentialProcess actionRef && actionRef.ProcessId == null }; this.InitializeItem(item, referential); dataItems.Add(item); } data.Items = dataItems.ToArray(); dataCol.Add(data); } // Une fois les items définis, les trier par ordre décroissant de valeurs Scenario referenceScenario = scenarios.FirstOrDefault(); if (referenceScenario != null) { var data = dataCol.First(d => d.Scenario == referenceScenario.Label); data.Items = data.Items.OrderByDescending(d => d.ReferentialDuration).ToArray(); var comparer = new ReferenceArrayComparer <string>(data.Items.Select(d => d.ReferentialName).ToArray()); foreach (var d in dataCol.Except(data)) { d.Items = d.Items.OrderBy(i => i.ReferentialName, comparer).ToArray(); } } switch (this.SelectedValueMode) { case RestitutionValueMode.Absolute: { // Calculer la durée totale du scénario initial long totalDuration = 0; foreach (var data in dataCol) { totalDuration = data.Items.Sum(i => i.ReferentialDuration); if (totalDuration != 0) { break; } } if (totalDuration != 0) { foreach (var data in dataCol) { foreach (var item in data.Items) { item.ReferentialPercentage = Convert.ToDouble(item.ReferentialDuration) / Convert.ToDouble(totalDuration) * 100.0; } } } } break; case RestitutionValueMode.Relative: { foreach (var data in dataCol) { var totalDuration = data.Items.Sum(i => i.ReferentialDuration); if (totalDuration != 0) { foreach (var item in data.Items) { item.ReferentialPercentage = Convert.ToDouble(item.ReferentialDuration) / Convert.ToDouble(totalDuration) * 100.0; } } } } break; case RestitutionValueMode.Occurences: break; default: throw new ArgumentOutOfRangeException("ValueMode"); } // Calculer les totaux total = new TData[scenarios.Length]; int ii = 0; foreach (var sc in scenarios) { var data = new TData() { Scenario = sc.Label }; var item = new TDataItem() { ReferentialName = LocalizationManager.GetString("ViewModel_Restitution_Total"), ReferentialDuration = dataCol[ii].Items.Sum(i => i.ReferentialDuration), ReferentialPercentage = dataCol[ii].Items.Sum(i => i.ReferentialPercentage), ReferentialOccurrences = dataCol[ii].Items.Sum(i => i.ReferentialOccurrences), ValueMode = this.SelectedValueMode, }; data.Items = new TDataItem[] { item }; total[ii] = data; ii++; } return(dataCol); }
/// <summary> /// Met à jour les actions récursivement sur les scénarios dérivés de celui spécifié. /// </summary> /// <param name="context">Le contexte EF.</param> /// <param name="sourceScenario">Le scénario source.</param> /// <param name="allScenarios">Tous les scénarios qui peuvent être impactés.</param> /// <param name="actionsToRemove">Les actions à supprimer manuellement.</param> internal static void UpdateActions(KsmedEntities context, Scenario sourceScenario, Scenario[] allScenarios, out KAction[] actionsToRemove, out IList <KAction> actionsWithOriginal) { var derivedScenarios = ScenarioActionHierarchyHelper.GetDerivedScenarios(sourceScenario, allScenarios); var actions = GetActionsSortedWBS(sourceScenario); actionsWithOriginal = new List <KAction>(); foreach (var scenario in derivedScenarios) { // Mettre à jour IsGroup foreach (var action in scenario.Actions) { action.IsGroup = WBSHelper.HasChildren(action, scenario.Actions); } } foreach (var originalAction in actions) { // J'enlève le IsMArkedAsModified car les 2 références sont la sauvegarde des actions depuis la construction et depuis l'optimisation // Or depuis la construction, en modification, le bout de code cidessous est déjà appelé // Et depuis l'optimisation, il n'y a pas de changement de temps video if (originalAction.IsMarkedAsAdded /*|| originalAction.IsMarkedAsModified*/) { var originalValues = originalAction.ChangeTracker.OriginalValues; var modifiedValues = originalAction.ChangeTracker.ModifiedValues; if (originalAction.IsMarkedAsAdded || modifiedValues.ContainsKey(ActionsTimingsMoveManagement.KActionStartPropertyName) || modifiedValues.ContainsKey(ActionsTimingsMoveManagement.KActionFinishPropertyName)) { // Vérifier si le temps vidéo a changé ActionsTimingsMoveManagement.GetOrignalModifiedVideoDurations(originalAction, out long originalDuration, out long modifiedDuration); bool hasVideoDurationChanged = originalDuration != modifiedDuration; // Si c'est une tâche créée et non dupliquée, le buildDuration est à 0, donc on doit le mettre à jour //Sinon, si c'est une tâche dupliquée, on le laisse tel quel. if (originalAction.BuildDuration == 0) { var paceRating = originalAction.Resource != null ? originalAction.Resource.PaceRating : 1d; originalAction.BuildDuration = Convert.ToInt64(modifiedDuration * paceRating); } } } if (originalAction.IsMarkedAsAdded) { // Si l'action est une action nouvelle dans un scénario cible, définir automatiquement la partie réduite if (sourceScenario.NatureCode == KnownScenarioNatures.Target && originalAction.Reduced == null) { SharedScenarioActionsOperations.ApplyNewReduced(originalAction); } var originalActionKey = context.CreateEntityKey(KsmedEntities.KActionsEntitySetName, originalAction); var parentOriginalAction = WBSHelper.GetParent(originalAction, actions); foreach (var derivedScenario in derivedScenarios) { var derivedActions = GetActionsSortedWBS(derivedScenario); // Rechercher le parent dans le scénario dérivé var parentDerivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(parentOriginalAction, derivedScenario); // Cloner l'action originale var newAction = ScenarioCloneManager.CloneAction(originalAction, ActionCloneBehavior.Cascade); // Assigner l'original var originalActionForCurrentDerivedScenario = derivedScenario.Original == sourceScenario ? originalAction : ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario.Original); newAction.Original = originalActionForCurrentDerivedScenario; actionsWithOriginal.Add(newAction); // Insérer l'action clonée dans le scénario dérivé ActionsTimingsMoveManagement.InsertUpdateWBS( derivedActions, newAction, parentDerivedAction, WBSHelper.GetParts(originalAction.WBS).Last(), (a, wbs) => EnsureTracking(a)); // Rafraichir les actions derivedScenario.Actions.Add(newAction); derivedActions = GetActionsSortedWBS(derivedScenario); // Ajouter les mêmes prédécesseurs et successeurs foreach (var originalPredecessor in originalAction.Predecessors) { var derivedPredecessor = ScenarioActionHierarchyHelper.GetDerivedAction(originalPredecessor, derivedScenario); if (derivedPredecessor != null) { EnsureTracking(derivedPredecessor); ActionsTimingsMoveManagement.AddPredecessor(derivedActions, newAction, derivedPredecessor); } } foreach (var originalSuccessor in originalAction.Successors) { var derivedSuccessor = ScenarioActionHierarchyHelper.GetDerivedAction(originalSuccessor, derivedScenario); if (derivedSuccessor != null) { EnsureTracking(derivedSuccessor); ActionsTimingsMoveManagement.AddPredecessor(derivedActions, derivedSuccessor, newAction); } } EnsureTracking(derivedScenario); SharedScenarioActionsOperations.EnsureEmptySolutionExists(derivedScenario); SharedScenarioActionsOperations.UdpateSolutionsApprovedState(derivedScenario); ActionsTimingsMoveManagement.DebugCheckAllWBS(derivedActions); } } else if (originalAction.IsMarkedAsModified) { var originalValues = originalAction.ChangeTracker.OriginalValues; var modifiedValues = originalAction.ChangeTracker.ModifiedValues; var propertiesToCopyValues = new Dictionary <string, object>(); foreach (var propertyName in _kActionPropertyNamesToCopy) { if (modifiedValues.ContainsKey(propertyName)) { propertiesToCopyValues[propertyName] = modifiedValues[propertyName]; } } // Vérifier si les reduced doit être impactés également ActionsTimingsMoveManagement.GetOrignalModifiedBuildDurations(originalAction, out long originalDuration, out long modifiedDuration); bool hasBuildDurationChanged = originalDuration != modifiedDuration; foreach (var derivedScenario in derivedScenarios) { var derivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario); if (derivedAction != null) { EnsureTracking(derivedAction); foreach (var kvp in propertiesToCopyValues) { derivedAction.SetPropertyValue(kvp.Key, kvp.Value); } if (hasBuildDurationChanged) { if (derivedAction.IsReduced) { // Modifier l'original duration et recalculer le temps final en fonction du gain EnsureTracking(derivedAction.Reduced); derivedAction.Reduced.OriginalBuildDuration = modifiedDuration; ActionsTimingsMoveManagement.UpdateTimingsFromReducedReduction(derivedAction); } else { // Simplement recopier la durée derivedAction.BuildDuration = modifiedDuration; } } } } } } var toRemove = new List <KAction>(); // Gérer les actions supprimées // EF gérant mal l'ordre des suppressions, ça créer une ConstraintException sur la FK OriginalActionId // Malheureusement un CascadeDelete est impossible puisque la FK est sur un même table if (sourceScenario.ChangeTracker.ObjectsRemovedFromCollectionProperties.ContainsKey("Actions")) { var removedActions = sourceScenario.ChangeTracker.ObjectsRemovedFromCollectionProperties["Actions"].ToArray(); foreach (KAction originalAction in removedActions) { EnsureTracking(originalAction); toRemove.Add(originalAction); originalAction.MarkAsUnchanged(); foreach (var derivedScenario in derivedScenarios) { var derivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario); if (derivedAction != null) { var derivedActions = GetActionsSortedWBS(derivedScenario); // Mettre à jour les WBS des autres actions ActionsTimingsMoveManagement.DeleteUpdateWBS(derivedActions, derivedAction, (a, wbs) => EnsureTracking(a)); EnsureTracking(derivedAction); toRemove.Add(derivedAction); } } } // Il faut maintenant trier les actions à supprimer pour que la suppression se fasse dans le bon ordre toRemove.Reverse(); actionsToRemove = toRemove.ToArray(); } else { actionsToRemove = new KAction[] { } }; sourceScenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(sourceScenario); foreach (var scenario in derivedScenarios) { EnsureTracking(scenario); ActionsTimingsMoveManagement.FixPredecessorsSuccessorsTimings(scenario.Actions.ToArray(), false); ActionsTimingsMoveManagement.UpdateVideoGroupsTiming(scenario.Actions.ToArray()); ActionsTimingsMoveManagement.UpdateBuildGroupsTiming(scenario.Actions.ToArray()); scenario.CriticalPathIDuration = ActionsTimingsMoveManagement.GetInternalCriticalPathDuration(scenario); } }
public void Delete_From_Input_TestCases() { var jsonObject = JObject.Parse(File.ReadAllText("ActionsTimingsMoveManagement_DeleteTestCases.json")); JArray cases = (JArray)jsonObject["delete"]; var testCases = JsonConvert.DeserializeObject <DeleteTestCase[]>(cases.ToString()); foreach (var tc in testCases) { TestContext.WriteLine("---------------------------------"); TestContext.WriteLine("Analyzing Test Case {0}", tc.Label); var inputTasks = new List <KAction>(); var wbses = tc.Input.Split(' '); foreach (var wbs in wbses) { var task = new KAction { Label = "T" + wbs, WBS = wbs, }; inputTasks.Add(task); } TestContext.WriteLine("Input :", tc.Label); WriteTasksWBS(inputTasks); TestContext.WriteLine("Delete : {0}", tc.Delete); var tasksToDelete = inputTasks.Where(t => tc.Delete.Any(wbs => wbs == t.WBS)).ToArray(); var outputTasks = inputTasks.ToList(); foreach (var task in tasksToDelete) { TestContext.WriteLine("Deleting : {0}", tc.Delete); ActionsTimingsMoveManagement.DeleteUpdateWBS(outputTasks.ToArray(), task); outputTasks.Remove(task); WriteTasksWBS(outputTasks); ActionsTimingsMoveManagement.DebugCheckAllWBS(outputTasks); } TestContext.WriteLine("Output :", tc.Label); WriteTasksWBS(outputTasks); TestContext.WriteLine("Expected :", tc.Label); WriteTasksWBS(tc.Expected); int i = 0; foreach (var expectedWbs in tc.Expected.Keys) { var expectedLabel = tc.Expected[expectedWbs]; if (outputTasks.Count - 1 < i) { Assert.Fail("The actual has less tasks than expected"); } var task = outputTasks[i]; var actualWbs = task.WBS; var actuallabel = task.Label; Assert.AreEqual(expectedWbs, task.WBS, string.Format("Expected {0}: {1}, Found {2}: {3}", expectedWbs, expectedLabel, actualWbs, actuallabel)); Assert.AreEqual(expectedLabel, actuallabel, string.Format("Expected {0}: {1}, Found {2}: {3}", expectedWbs, expectedLabel, actualWbs, actuallabel)); i++; } if (i != outputTasks.Count) { Assert.Fail("The actual has more tasks than expected"); } } }
/// <summary> /// Enregistre des actions déjà existantes et construit les éléments. /// </summary> /// <param name="allActions">Toutes les actions.</param> /// <param name="filter">Les filtre I/E/S.</param> public void RegisterInitialActions(IEnumerable <KAction> allActions, IESFilterValue filter) { Func <ActionsDisplayResults> displayedActions; bool isCriticalPathEnabled; bool useManagedPredSucc; bool useGanttItemsTimingsOnly; bool fixPredSuccTimings; bool refreshTimings; foreach (var action in allActions) { action.IsGroup = WBSHelper.HasChildren(action, allActions); action.IsLinkToProcess = action.LinkedProcessId != null; } switch (filter) { case IESFilterValue.IES: useManagedPredSucc = false; isCriticalPathEnabled = true; displayedActions = () => new ActionsDisplayResults { Actions = allActions }; useGanttItemsTimingsOnly = false; fixPredSuccTimings = true; refreshTimings = true; break; case IESFilterValue.I: useManagedPredSucc = true; isCriticalPathEnabled = true; displayedActions = () => FilterActionsUpdatePredSuccManaged(allActions, a => ActionsTimingsMoveManagement.IsActionExternal(a) || ActionsTimingsMoveManagement.IsActionDeleted(a), a => ActionsTimingsMoveManagement.IsActionInternal(a)); useGanttItemsTimingsOnly = true; fixPredSuccTimings = true; refreshTimings = false; break; case IESFilterValue.IE: useManagedPredSucc = true; isCriticalPathEnabled = false; displayedActions = () => FilterActionsUpdatePredSuccManaged(allActions, a => ActionsTimingsMoveManagement.IsActionDeleted(a), a => ActionsTimingsMoveManagement.IsActionExternal(a) || ActionsTimingsMoveManagement.IsActionInternal(a)); useGanttItemsTimingsOnly = true; fixPredSuccTimings = true; refreshTimings = false; break; default: throw new ArgumentOutOfRangeException("filter"); } _useGanttItemsTimingsOnly = useGanttItemsTimingsOnly; this.IsCriticalPathEnabled = isCriticalPathEnabled; var results = displayedActions(); this.UseManagedPredecessorsSuccessors = useManagedPredSucc; if (filter == IESFilterValue.IES && useManagedPredSucc) { ActionsTimingsMoveManagement.DebugCheckAllWBS(results.Actions); } this.RegisterInitialActionsImpl(results.Actions); if (results.NewTimings != null) { foreach (var timing in results.NewTimings) { SetBuildStart(timing.Action, timing.Start); SetBuildFinish(timing.Action, timing.Finish); } } if (refreshTimings) { foreach (var item in this.ItemsOfTypeAction) { _updatingitem = item; item.Start = GanttDates.ToDateTime(item.Action.BuildStart); item.Finish = GanttDates.ToDateTime(item.Action.BuildFinish); _updatingitem = null; } } if (fixPredSuccTimings) { this.FixPredecessorsSuccessorsTimings(); this.UpdateResourcesLoad(); } }
public void TestsIsActionInternalExternalDeletedMutuallyExclusive() { SampleData.ClearDatabaseThenImportDefaultProject(); // Vérifie dans toutes les actions de la BDD si les actions sont soi I, E ou S et jamais aucun des trois ou plus d'un des 3 à la fois. var prepareService = new PrepareService(); var analyzeService = new AnalyzeService(); var mre = new System.Threading.ManualResetEvent(false); ProjectsData pData = null; Exception exception = null; prepareService.GetProjects(d => { pData = d; mre.Set(); }, ex => { exception = ex; mre.Set(); }); mre.WaitOne(); AssertExt.IsExceptionNull(exception); foreach (var project in pData.Projects) { mre.Reset(); RestitutionData rData = null; analyzeService.GetFullProjectDetails(project.ProjectId, d => { rData = d; mre.Set(); }, ex => { exception = ex; mre.Set(); }); mre.WaitOne(); AssertExt.IsExceptionNull(exception); foreach (var scenario in rData.Scenarios) { foreach (var action in scenario.Actions) { bool i = ActionsTimingsMoveManagement.IsActionInternal(action); bool e = ActionsTimingsMoveManagement.IsActionExternal(action); bool s = ActionsTimingsMoveManagement.IsActionDeleted(action); Assert.IsTrue(i || e || s); if (i) { Assert.IsFalse(e || s); } else if (e) { Assert.IsFalse(i || s); } else { Assert.IsFalse(i || e); } } } } }
public void BulkScenarioCloneTests() { SampleData.ClearDatabaseThenImportDefaultProject(); var service = new PrepareService(); int[] projectIds; using (var context = KProcess.Ksmed.Data.ContextFactory.GetNewContext()) { projectIds = context.Projects.Select(p => p.ProjectId).ToArray(); } foreach (var pid in projectIds) { var mre = new System.Threading.ManualResetEvent(false); Exception e = null; ScenariosData data = null; service.GetScenarios(pid, d => { data = d; mre.Set(); }, ex => { e = ex; mre.Set(); }); mre.WaitOne(); AssertExt.IsExceptionNull(e); Assert.IsNotNull(data); foreach (var scenario in data.Scenarios.Where(s => s.NatureCode != KnownScenarioNatures.Realized)) { if (scenario.NatureCode == KnownScenarioNatures.Target && scenario.Actions.Any(a => !a.IsReduced)) { // Il s'agit d'un vieux projet. Tous les actions d'un scénario cible doivent aujourd'hui avoir une partie réduite continue; } mre.Reset(); Scenario newScenario = null; service.CreateScenario(pid, scenario, true, s => { newScenario = s; mre.Set(); }, ex => { e = ex; mre.Set(); }); mre.WaitOne(); AssertExt.IsExceptionNull(e); Assert.IsNotNull(newScenario); // Vérification de l'intégrité du scénario ActionsTimingsMoveManagement.DebugCheckAllWBS(EnumerableExt.Concat(newScenario)); // Vérifier qu'il n'y ai pas de tâche avec un temps process nul if (scenario.NatureCode != KnownScenarioNatures.Initial && newScenario.Actions.Any(a => a.BuildDuration <= 0)) { Assert.Fail("Une action a un temps invalide"); } } } }
/// <summary> /// Supprime les actions dont la durée est 0 du scénario. /// Supprime également les groupes vides qui pourraient résulter des suppressions précédentes. /// </summary> /// <param name="scenario">Le scénario.</param> internal static void RemoveEmptyDurationActionsAndGroupsFromNewScenario(Scenario scenario) { var emptyDurationActionsToDelete = new List <KAction>(); var newTimings = new List <ActionTiming>(); var derivedActions = GetActionsSortedWBS(scenario); // Mettre à jour IsGroup foreach (var action in derivedActions) { action.IsGroup = WBSHelper.HasChildren(action, derivedActions); } foreach (var action in derivedActions) { //On ne supprime pas si: la catégorie de type à supprimer && pas de parent dans le scenario n && originalBuildDuration!=0 if (action.BuildDuration == 0 && !action.IsGroup && !(action.Category != null && action.Category.ActionTypeCode != null && action.Category.ActionTypeCode == KnownActionCategoryTypes.S && action.Original.Original == null && action.Reduced != null && action.Reduced.OriginalBuildDuration != 0)) { // Déplacer tous les pred vers ses succ ActionsTimingsMoveManagement.MapAllPredToSucc(action, a => a.BuildDuration != 0 && !action.IsGroup, newTimings, false); emptyDurationActionsToDelete.Add(action); scenario.Actions.Remove(action); action.Predecessors.Clear(); action.Successors.Clear(); action.MarkAsDeleted(); } } //foreach (var action in actionsToDelete) //{ // // Mettre à jour les WBS des autres actions // ActionsTimingsMoveManagement.DeleteUpdateWBS(derivedActions, action); //} if (scenario.NatureCode == KnownScenarioNatures.Realized) { foreach (var timing in newTimings) { timing.Action.Start = timing.Start; timing.Action.Finish = timing.Finish; } } //actionsToDelete.Clear(); var emptyGroupActionsToDelete = new List <KAction>(); var actionsFiltered = GetActionsSortedWBS(scenario); foreach (var action in actionsFiltered) { if (action.IsGroup && !WBSHelper.HasChildren(action, actionsFiltered)) { emptyGroupActionsToDelete.Add(action); scenario.Actions.Remove(action); action.MarkAsDeleted(); } } //foreach (var action in emptyGroupActionsToDelete) //{ // // Mettre à jour les WBS des autres actions // ActionsTimingsMoveManagement.DeleteUpdateWBS(derivedActions, action); //} // Mettre à jour les WBS des autres actions var tree = derivedActions.VirtualizeTree(); emptyDurationActionsToDelete .Union(emptyGroupActionsToDelete) .ForEach(_ => tree.Remove(_)); tree.ApplyWBS(); ActionsTimingsMoveManagement.DebugCheckAllWBS(EnumerableExt.Concat(scenario)); ActionsTimingsMoveManagement.DebugCheckPredSucc(scenario.Actions, false); }
/// <summary> /// Prédit les scénarios qui seront impactés par les modifications en attente de sauvegarde. /// </summary> /// <param name="sourceModifiedScenario">Le scenério source modifié.</param> /// <param name="allScenarios">Tous les scénarios.</param> /// <param name="actionsToDelete">Les actions à supprimer.</param> /// <returns> /// Les scénarios impactés. /// </returns> internal static Scenario[] PredictImpactedScenarios(Scenario sourceModifiedScenario, Scenario[] allScenarios, KAction[] actionsToDelete, KAction[] actionsWithUpdatedWBS) { var derivedScenarios = ScenarioActionHierarchyHelper.GetDerivedScenarios(sourceModifiedScenario, allScenarios); var scenariosToInspect = new List <Scenario>(derivedScenarios); var actions = GetActionsSortedWBS(sourceModifiedScenario); if (actions.Any(a => a.IsMarkedAsAdded)) { scenariosToInspect.Clear(); } else { foreach (var originalAction in actions) { bool hasUpdatedWBS = actionsWithUpdatedWBS != null && actionsWithUpdatedWBS.Contains(originalAction); if (originalAction.IsMarkedAsModified || hasUpdatedWBS) { var modifiedValues = originalAction.ChangeTracker.ModifiedValues; // S'il y a une correspondance entre le nom d'une propriété modifiée et les propriétés qui sont impactantes if (hasUpdatedWBS || modifiedValues.Keys.Intersect(_kActionPropertyNamesToCopy).Any()) { // rechercher tous les scénarios qui possèdent ces actions dérivées foreach (var derivedScenario in scenariosToInspect.ToArray()) { var derivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario); if (derivedAction != null) { scenariosToInspect.Remove(derivedScenario); } if (!scenariosToInspect.Any()) { break; } } } if (modifiedValues.ContainsKey(ActionsTimingsMoveManagement.KActionBuildStartPropertyName) || modifiedValues.ContainsKey(ActionsTimingsMoveManagement.KActionBuildFinishPropertyName)) { ActionsTimingsMoveManagement.GetOrignalModifiedBuildDurations(originalAction, out long originalDuration, out long modifiedDuration); if (modifiedDuration != originalDuration) { // rechercher tous les scénarios qui possèdent ces actions dérivées foreach (var derivedScenario in scenariosToInspect.ToArray()) { var derivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario); if (derivedAction != null && derivedAction.IsReduced) { scenariosToInspect.Remove(derivedScenario); } if (!scenariosToInspect.Any()) { break; } } } } } } } if (actionsToDelete != null) { foreach (var originalAction in actionsToDelete) { // rechercher tous les scénarios qui possèdent ces actions dérivées foreach (var derivedScenario in scenariosToInspect.ToArray()) { var derivedAction = ScenarioActionHierarchyHelper.GetDerivedAction(originalAction, derivedScenario); if (derivedAction != null) { scenariosToInspect.Remove(derivedScenario); } } if (!scenariosToInspect.Any()) { break; } } } return(derivedScenarios.Except(scenariosToInspect).ToArray()); }
/// <summary> /// Exporte les actions spécifiées. /// </summary> /// <param name="actions">Les actions.</param> /// <param name="cellReference">La référence de la cellule.</param> /// <param name="sheet">La feuille.</param> private void ExportActions(ICollection <KAction> actions, WorksheetPart sheet, ref CellReference cellReference) { var timeFormatService = IoC.Resolve <IServiceBus>().Get <ITimeTicksFormatService>(); var timeScale = _data.Project.TimeScale; #region Format pour les actions // Actions var actionsFormats = new List <ColumnFormat>() { // Thumbnail new ColumnFormat() { Header = string.Empty }, // Label new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Task") }, // Start new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Start") }, // Duration new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Duration") }, // Finish new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Finish") }, // BuildStart new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_BuildStart") }, // BuildDuration new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_BuildDuration") }, // BuildFinish new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_BuildFinish") }, // WBS new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_WBS") }, // Category new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Category") }, // Resource new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Resource") }, // Video new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Video") }, // Predecessors new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Predecessors") }, // Original new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Original") }, }; // Ref1 if (_referentialsUse[ProcessReferentialIdentifier.Ref1].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref1) }); } // Ref2 if (_referentialsUse[ProcessReferentialIdentifier.Ref2].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref2) }); } // Ref3 if (_referentialsUse[ProcessReferentialIdentifier.Ref3].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref3) }); } // Ref4 if (_referentialsUse[ProcessReferentialIdentifier.Ref4].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref4) }); } // Ref5 if (_referentialsUse[ProcessReferentialIdentifier.Ref5].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref5) }); } // Ref6 if (_referentialsUse[ProcessReferentialIdentifier.Ref6].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref6) }); } // Ref7 if (_referentialsUse[ProcessReferentialIdentifier.Ref7].IsEnabled) { actionsFormats.Add(new ColumnFormat() { Header = IoC.Resolve <IReferentialsUseService>().GetLabel(ProcessReferentialIdentifier.Ref7) }); } // IsRandom actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_IsRandom") }); // Custom Text actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Text1") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Text2") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Text3") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Text4") }); // Custom Numeric actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Numeric1") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Numeric2") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Numeric3") }); actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_CustomValue_Numeric4") }); // DifferenceReason actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_DifferenceReason") }); // Amélioration I/E/S actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_IES") }); // Solution actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_Solution") }); // Reduction ratio actionsFormats.Add(new ColumnFormat() { Header = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_ReductionRatio") }); #endregion CellContent[][] data = new CellContent[actions.Count][]; uint tableRowIndex = cellReference.RowIndex; int i = 0; foreach (var action in actions) { CellContent[] row = new CellContent[actionsFormats.Count]; // Mettre à jour IsGroup action.IsGroup = WBSHelper.HasChildren(action, actions); int j = 0; #region Data pour les actions // Thumbnail j++; // Label row[j++] = action.Label; if (!action.IsGroup) { // Start row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.Start, timeScale)); // Duration row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.Duration, timeScale)); // Finish row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.Finish, timeScale)); } else { j += 3; // Nombre de colonnes précédentes } // BuildStart row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.BuildStart, timeScale)); // BuildDuration row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.BuildDuration, timeScale)); // BuildFinish row[j++] = CellContent.TimeSpan(timeFormatService.RoundTime(action.BuildFinish, timeScale)); // WBS row[j++] = action.WBS; if (!action.IsGroup) { // Category if (action.Category != null) { row[j++] = action.Category.Label; } else { row[j++] = null; } // Resource if (action.Resource != null) { row[j++] = action.Resource.Label; } else { row[j++] = null; } // Video if (action.Video != null) { row[j++] = action.Video.Filename; } else { row[j++] = null; } // Predecessors row[j++] = FormatPredecessorsString(action); // Original if (action.Original != null) { row[j++] = action.Original.Label; } else { row[j++] = null; } // Ref1 if (_referentialsUse[ProcessReferentialIdentifier.Ref1].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref1, ProcessReferentialIdentifier.Ref1); } // Ref2 if (_referentialsUse[ProcessReferentialIdentifier.Ref2].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref2, ProcessReferentialIdentifier.Ref2); } // Ref3 if (_referentialsUse[ProcessReferentialIdentifier.Ref3].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref3, ProcessReferentialIdentifier.Ref3); } // Ref4 if (_referentialsUse[ProcessReferentialIdentifier.Ref4].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref4, ProcessReferentialIdentifier.Ref4); } // Ref5 if (_referentialsUse[ProcessReferentialIdentifier.Ref5].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref5, ProcessReferentialIdentifier.Ref5); } // Ref6 if (_referentialsUse[ProcessReferentialIdentifier.Ref6].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref6, ProcessReferentialIdentifier.Ref6); } // Ref7 if (_referentialsUse[ProcessReferentialIdentifier.Ref7].IsEnabled) { row[j++] = GetMultiReferentialLabels(action.Ref7, ProcessReferentialIdentifier.Ref7); } // IsRandom row[j++] = action.IsRandom.ToString(); // Custom Text row[j++] = action.CustomTextValue; row[j++] = action.CustomTextValue2; row[j++] = action.CustomTextValue3; row[j++] = action.CustomTextValue4; // Custom Numeric row[j++] = action.CustomNumericValue; row[j++] = action.CustomNumericValue2; row[j++] = action.CustomNumericValue3; row[j++] = action.CustomNumericValue4; // DifferenceReason row[j++] = action.DifferenceReason; // Amélioration if (action.Reduced != null) { // Amélioration I/E/S string label; if (ActionsTimingsMoveManagement.IsActionInternal(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_Internal"); } else if (ActionsTimingsMoveManagement.IsActionExternal(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_External"); } else if (ActionsTimingsMoveManagement.IsActionDeleted(action)) { label = LocalizationManager.GetString("ViewModel_AnalyzeRestitution_Export_Action_Reduced_Deleted"); } else { throw new ArgumentOutOfRangeException(); } row[j++] = label; row[j++] = action.Reduced.Solution; row[j++] = CellContent.Percentage(action.Reduced.ReductionRatio); } else { row[j++] = null; row[j++] = null; row[j++] = null; } } #endregion data[i] = row; i++; } _file.AddTable(sheet, actionsFormats.ToArray(), data, cellReference); // Ajouter une image cellReference.NewLine(); i = 0; foreach (var action in actions) { if (action.Thumbnail != null) { // Ajout du libellé du lien var tableCellRef = new CellReference(1, tableRowIndex + (uint)i + 1); _file.SetCellValue(sheet, tableCellRef, new CellContent(action.ActionId.ToString(), CellDataType.Hyperlink)); // Ajout du lien string definedName = string.Format("Action.{0}", action.ActionId); _file.CreateDefinedName(sheet, cellReference, definedName); _file.AddHyperlinkToDefinedName(sheet, tableCellRef, definedName, ""); // Ajout du libellé _file.SetCellValue(sheet, cellReference, string.Format("{0} {1}", action.WBS, action.Label ?? string.Empty)); cellReference.MoveRight(); // Ajout de l'image using (var ms = new System.IO.MemoryStream(action.Thumbnail.Data)) { var decoder = System.Windows.Media.Imaging.BitmapDecoder.Create(ms, System.Windows.Media.Imaging.BitmapCreateOptions.PreservePixelFormat, System.Windows.Media.Imaging.BitmapCacheOption.OnLoad); var size = new System.Windows.Size(decoder.Frames[0].PixelWidth, decoder.Frames[0].PixelHeight); var mimeTypes = decoder.CodecInfo.MimeTypes; ImagePartType imageType; if (mimeTypes.Contains("image/jpeg")) { imageType = ImagePartType.Jpeg; } else if (mimeTypes.Contains("image/bmp")) { imageType = ImagePartType.Bmp; } else if (mimeTypes.Contains("image/png")) { imageType = ImagePartType.Png; } else { continue; } string pictureName = string.Format("Thumbnail.{0}", action.ActionId); uint rowsLength = _file.AddImage(sheet, action.Thumbnail.Data, imageType, action.ActionId.ToString(), pictureName, size, cellReference); // On déplace la cellule active à après l'image cellReference = new CellReference(1, cellReference.RowIndex + rowsLength); } } i++; } }
/// <summary> /// Filtre les actions et met à jour leurs prédécesseurs et succésseurs managés. /// </summary> /// <param name="allActions">Toutes les actions.</param> /// <param name="excludedActionsFilter">Le filtre excluant les actions.</param> /// <param name="includedActionsPredSuccFilter">Le filtre incluant les actions prédécesseurs et succ.</param> /// <returns>Les actions filtrées.</returns> private ActionsDisplayResults FilterActionsUpdatePredSuccManaged(IEnumerable <KAction> allActions, Func <KAction, bool> excludedActionsFilter, Func <KAction, bool> includedActionsPredSuccFilter) { var results = new ActionsDisplayResults(); var newTimings = new List <ActionTiming>(); var actionsSorted = allActions .OrderBy(a => a.WBSParts, new WBSHelper.WBSComparer()) .ToArray(); #if DEBUG foreach (var action in allActions) { // S'assurer qu'une action soit un seul de : I/E/S/Group var isI = ActionsTimingsMoveManagement.IsActionInternal(action); var isE = ActionsTimingsMoveManagement.IsActionExternal(action); var isS = ActionsTimingsMoveManagement.IsActionDeleted(action); var isG = action.IsGroup; if (!(isI | isE | isS | isG)) { throw new InvalidOperationException("Impossible qu'une action soit à la fois I ou E ou S ou G"); } } #endif var actionsFinal = new List <KAction>(); foreach (var action in actionsSorted) { action.PredecessorsManaged.Clear(); action.SuccessorsManaged.Clear(); } foreach (var action in actionsSorted) { if (excludedActionsFilter(action)) { // Déplacer tous les pred vers ses succ ActionsTimingsMoveManagement.MapAllPredToSucc(action, includedActionsPredSuccFilter, newTimings, true); } else if (!action.IsGroup) { foreach (var pred in action.Predecessors) { if (includedActionsPredSuccFilter(pred)) { pred.SuccessorsManaged.Add(action); action.PredecessorsManaged.Add(pred); } } actionsFinal.Add(action); } else { System.Diagnostics.Debug.Assert(action.IsGroup); actionsFinal.Add(action); } } // Supprimer les liens pour les actions non présentes dans la collection foreach (var action in actionsFinal) { foreach (var pred in action.PredecessorsManaged.ToArray()) { if (!actionsFinal.Contains(pred)) { action.PredecessorsManaged.Remove(pred); pred.SuccessorsManaged.Remove(action); } } foreach (var succ in action.SuccessorsManaged.ToArray()) { if (!actionsFinal.Contains(succ)) { action.SuccessorsManaged.Remove(succ); succ.PredecessorsManaged.Remove(action); } } } // Il faut automatiquement supprimer les groupes qui ne contiennent plus d'enfants // Les groupes n'ayant ni précédesseurs ni successeurs, on peut les supprimer sans réel impact var copy = actionsFinal.ToArray(); foreach (var action in copy) { if (action.IsGroup && !WBSHelper.HasChildren(action, copy)) { actionsFinal.Remove(action); } } results.Actions = actionsFinal; results.NewTimings = newTimings; return(results); }