source: branches/2892_LR-prediction-intervals/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs @ 16388

Last change on this file since 16388 was 16388, checked in by gkronber, 2 years ago

#2892: Merging r15750:16382 (HEAD) from trunk to branch, resolving conflicts

File size: 20.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using HeuristicLab.Clients.Access;
28using HeuristicLab.Common.Resources;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31using System.Collections;
32using HeuristicLab.Common;
33
34namespace HeuristicLab.Clients.Hive.Administrator.Views {
35  [View("ProjectView")]
36  [Content(typeof(Project), IsDefaultView = false)]
37  public partial class ProjectResourcesView : ItemView {
38    private const int slaveImageIndex = 0;
39    private const int slaveGroupImageIndex = 1;
40    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
41    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
42    public const string IMMUTABLE_TAG = " [assigned, immutable]";
43    public const string INCLUDED_TAG = " [included]";
44    public const string ADDED_ASSIGNMENT_TAG = " [added assignment]";
45    public const string REMOVED_ASSIGNMENT_TAG = " [removed assignment]";
46    public const string ADDED_INCLUDE_TAG = " [added include]";
47    public const string REMOVED_INCLUDE_TAG = " [removed include]";
48
49    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
50    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
51    private readonly HashSet<Resource> includedResources = new HashSet<Resource>();
52    private readonly HashSet<Resource> newIncludedResources = new HashSet<Resource>();
53
54    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
55    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
56    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
57    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
58    private readonly Color selectedBackColor = Color.DodgerBlue;
59    private readonly Color selectedForeColor = Color.White;
60    private readonly Color grayTextColor = SystemColors.GrayText;
61
62    private HashSet<Resource> projectExclusiveResources = new HashSet<Resource>();
63    private TreeNode ungroupedGroupNode;
64
65    public new Project Content {
66      get { return (Project)base.Content; }
67      set { base.Content = value; }
68    }
69
70    public ProjectResourcesView() {
71      InitializeComponent();
72
73      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
74      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
75
76      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
77      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
78    }
79
80    #region Overrides
81    protected override void OnClosing(FormClosingEventArgs e) {
82      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
83      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
84      base.OnClosing(e);
85    }
86
87    protected override void OnContentChanged() {
88      base.OnContentChanged();
89      if (Content == null) {
90        assignedResources.Clear();
91        newAssignedResources.Clear();
92        includedResources.Clear();
93        treeView.Nodes.Clear();
94        detailsViewHost.Content = null;
95      } else {
96        UpdateAssignedResources();
97        UpdateIncludedResources();
98        detailsViewHost.Content = BuildResourceTree();
99      }
100      SetEnabledStateOfControls();
101    }
102
103    protected override void SetEnabledStateOfControls() {
104      base.SetEnabledStateOfControls();
105      bool enabled = Content != null && !Locked && !ReadOnly;
106
107      inheritButton.Enabled = enabled;
108      saveButton.Enabled = enabled;
109      treeView.Enabled = enabled;
110
111      if (detailsViewHost != null) {
112        detailsViewHost.Locked = true;
113      }
114    }
115    #endregion
116
117    #region Event Handlers
118    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
119      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
120      else {
121        SetEnabledStateOfControls();
122      }
123    }
124
125    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
126      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
127      else {
128        OnContentChanged();
129      }
130    }
131
132    private void ProjectResourcesView_Load(object sender, EventArgs e) {
133
134    }
135
136    private void refreshButton_Click(object sender, EventArgs e) {
137      HiveAdminClient.Instance.Refresh();
138      UpdateAssignedResources();
139      UpdateIncludedResources();
140      var top = BuildResourceTree();
141      detailsViewHost.Content = top;
142    }
143
144    private async void inheritButton_Click(object sender, EventArgs e) {
145      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
146        action: () => {
147          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false);
148        });
149      HiveAdminClient.Instance.Refresh();
150      UpdateResourceTree();
151    }
152
153    private async void saveButton_Click(object sender, EventArgs e) {
154      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
155        action: () => {
156          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false);
157        });
158      HiveAdminClient.Instance.Refresh();
159      UpdateResourceTree();
160    }
161
162    private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
163      if (e.Node == null || e.Node == ungroupedGroupNode)
164        e.Cancel = true;
165
166      var selectedResource = (Resource)e.Node.Tag;
167      if(HiveAdminClient.Instance.DisabledParentResources.Contains(selectedResource))
168        e.Cancel = true;
169    }
170
171    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
172      var selectedResource = (Resource)e.Node.Tag;
173      detailsViewHost.Content = selectedResource;
174    }
175
176    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
177      if (e.Node == null || e.Node == ungroupedGroupNode) {
178        e.Cancel = true;
179        return;
180      }
181       
182      var checkedResource = (Resource)e.Node.Tag;
183      if (checkedResource == null
184        || checkedResource.Id == Guid.Empty
185        || HiveAdminClient.Instance.DisabledParentResources.Contains(checkedResource)
186        || newIncludedResources.Contains(checkedResource)) {
187        e.Cancel = true;
188      } else if (!IsAdmin()) {
189          if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
190            || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
191            || projectExclusiveResources.Contains(checkedResource)) {
192            e.Cancel = true;
193          }
194      }
195    }
196
197    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
198      var checkedResource = (Resource)e.Node.Tag;
199      if (e.Node.Checked) {
200        newAssignedResources.Add(checkedResource);
201      } else {
202        newAssignedResources.Remove(checkedResource);
203      }
204
205      UpdateNewResourceTree();
206    }
207    #endregion
208
209    #region Helpers
210
211    private void UpdateResourceTree() {
212      UpdateAssignedResources();
213      UpdateIncludedResources();
214
215      var top = BuildResourceTree();
216      detailsViewHost.Content = top;
217    }
218
219    private void UpdateNewResourceTree() {
220      UpdateNewAssignedResources();
221      UpdateNewIncludedResources();
222      var top = BuildResourceTree();
223      detailsViewHost.Content = top;
224    }
225
226    private IEnumerable<Resource> GetAvailableResourcesForProjectAdministration(Guid projectId) {
227      projectExclusiveResources.Clear();
228      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
229      var resources = HiveAdminClient.Instance.Resources.Where(x => x.Id != Guid.Empty);
230      if (!resources.Any()) return resources;
231      if (IsAdmin()) return resources;
232
233      // get project specific assigned resources
234      var projectResources = resources.Where(x =>
235        HiveAdminClient.Instance.ProjectResourceAssignments
236          .Where(a => a.ProjectId == projectId)
237          .Select(a => a.ResourceId)
238        .Contains(x.Id));
239
240      // look up for assignments of ancestor projects
241      var projectIds = new HashSet<Guid>();
242      HiveAdminClient.Instance.GetAvailableProjectAncestors(projectId).ToList().ForEach(x => projectIds.Add(x.Id));
243
244      var ancestorProjectResources = resources.Where(x =>
245        HiveAdminClient.Instance.ProjectResourceAssignments
246          .Where(a => projectIds.Contains(a.ProjectId))
247          .Select(a => a.ResourceId)
248        .Contains(x.Id));
249
250      // look down for included resources of ancestor projects
251      HashSet<Resource> availableResources = new HashSet<Resource>(ancestorProjectResources);
252      foreach (var r in ancestorProjectResources) {
253        foreach(var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
254          availableResources.Add(d);
255        }
256      }
257
258      // determine resources, which are exclusively assigned to the currently selected project
259      projectResources
260        .Except(availableResources)
261        .ToList()
262        .ForEach(x => projectExclusiveResources.Add(x));
263
264      // look down for included resources of currently selected project
265      if (projectExclusiveResources.Any()) {
266        foreach (var r in projectExclusiveResources.ToArray()) {
267          foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
268            projectExclusiveResources.Add(d);
269          } 
270        }
271      }
272
273      return availableResources.Union(projectExclusiveResources);
274    }
275
276    private IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
277      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
278      return HiveAdminClient.Instance.Resources.Where(x =>
279        HiveAdminClient.Instance.ProjectResourceAssignments
280          .Where(a => a.ProjectId == projectId)
281          .Select(a => a.ResourceId)
282        .Contains(x.Id));
283    }
284
285    private void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
286      if (projectId == null || resourceIds == null) return;
287      HiveServiceLocator.Instance.CallHiveService(s => {
288       s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading);
289      });
290    }
291
292    private void UpdateNewAssignedResources() {
293      for(int i = newAssignedResources.Count -1; i >= 0; i--) {
294        if(newAssignedResources.Intersect(HiveAdminClient.Instance.GetAvailableResourceAncestors(newAssignedResources.ElementAt(i).Id)).Any()) {
295          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
296        }
297      }
298    }
299
300    private void UpdateAssignedResources() {
301      assignedResources.Clear();
302      newAssignedResources.Clear();
303      foreach (var r in GetAssignedResourcesForProject(Content.Id)) {
304        assignedResources.Add(r);
305        newAssignedResources.Add(r);
306      }
307    }
308
309    private void UpdateIncludedResources() {
310      includedResources.Clear();
311      newIncludedResources.Clear();
312      foreach (var a in assignedResources) {
313        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
314          includedResources.Add(r);
315          newIncludedResources.Add(r);
316        }
317      }
318    }
319
320    private void UpdateNewIncludedResources() {
321      newIncludedResources.Clear();
322      foreach (var a in newAssignedResources) {
323        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
324          newIncludedResources.Add(r);
325        }
326      }
327    }
328
329    private Resource BuildResourceTree() {
330      treeView.Nodes.Clear();
331
332      treeView.BeforeCheck -= treeView_BeforeCheck;
333      treeView.AfterCheck -= treeView_AfterCheck;
334
335      var resources = GetAvailableResourcesForProjectAdministration(Content.Id);
336
337      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
338      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
339      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
340      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
341      //mainResources.UnionWith(parentedMainResources);
342      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
343      mainResources.UnionWith(mainDisabledParentResources);
344      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
345
346      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
347     
348      Resource top = null;
349      //bool nodeSelected = false;
350      if (detailsViewHost != null && detailsViewHost.Content != null && detailsViewHost.Content is Resource) {
351        var resourceId = ((Resource)detailsViewHost.Content).Id;
352        top = resources.Where(x => x.Id == resourceId).FirstOrDefault();
353      }
354     
355
356      TreeNode currentNode = null;
357      Resource currentResource = null;
358
359      var addedAssignments = newAssignedResources.Except(assignedResources);
360      var removedAssignments = assignedResources.Except(newAssignedResources);
361      var addedIncludes = newIncludedResources.Except(includedResources);
362      var removedIncludes = includedResources.Except(newIncludedResources);
363
364      while (stack.Any()) {
365        var newResource = stack.Pop();
366        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
367
368        if(top == null && !disabledParentResources.Contains(newResource)) {
369          top = newResource;
370        }
371
372        //if(!nodeSelected && top != null && newResource.Id == top.Id) {
373        //  newNode.BackColor = selectedBackColor;
374        //  newNode.ForeColor = selectedForeColor;
375        //  nodeSelected = true;
376        //}
377
378        // search for parent node of newNode and save in currentNode
379        // necessary since newNodes (stack top items) might be siblings
380        // or grand..grandparents of previous node (currentNode)
381        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
382          currentNode = currentNode.Parent;
383          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
384        }
385
386        if (currentNode == null) {
387          treeView.Nodes.Add(newNode);
388        } else {
389          currentNode.Nodes.Add(newNode);
390        }
391
392        if (disabledParentResources.Contains(newResource)) {
393          newNode.Checked = false;
394          newNode.ForeColor = grayTextColor;
395        } else if (newAssignedResources.Contains(newResource)) {
396          newNode.Checked = true;
397          if(!HiveRoles.CheckAdminUserPermissions()) {
398            if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)
399              || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any()
400              || projectExclusiveResources.Contains(newResource)) {
401              newNode.ForeColor = SystemColors.GrayText;
402              newNode.Text += IMMUTABLE_TAG;
403            }
404          }
405
406        } else if (newIncludedResources.Contains(newResource)) {
407          newNode.Checked = true;
408          newNode.ForeColor = SystemColors.GrayText;
409        }
410
411        if (includedResources.Contains(newResource) && newIncludedResources.Contains(newResource)) {
412          newNode.Text += INCLUDED_TAG;
413        } else if (addedIncludes.Contains(newResource)) {
414          newNode.BackColor = addedIncludeColor;
415          newNode.ForeColor = SystemColors.GrayText;
416          newNode.Text += ADDED_INCLUDE_TAG;
417        } else if (removedIncludes.Contains(newResource)) {
418          newNode.BackColor = removedIncludeColor;
419          newNode.Text += REMOVED_INCLUDE_TAG;
420        }
421
422        if (addedAssignments.Contains(newResource)) {
423          newNode.BackColor = addedAssignmentColor;
424          newNode.ForeColor = SystemColors.ControlText;
425          newNode.Text += ADDED_ASSIGNMENT_TAG;
426        } else if (removedAssignments.Contains(newResource)) {
427          newNode.BackColor = removedAssignmentColor;
428          newNode.ForeColor = SystemColors.ControlText;
429          newNode.Text += REMOVED_ASSIGNMENT_TAG;
430        }
431
432        if (newResource is Slave) {
433          newNode.ImageIndex = slaveImageIndex;
434        } else {
435          newNode.ImageIndex = slaveGroupImageIndex;
436
437          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
438          if (childResources.Any()) {
439            foreach (var resource in childResources.OrderByDescending(x => x.Name)) {
440              subResources.Remove(resource);
441              stack.Push(resource);
442            }
443            currentNode = newNode;
444            currentResource = newResource;
445          }
446        }
447        newNode.SelectedImageIndex = newNode.ImageIndex;
448      }
449
450      ExpandResourceNodesOfInterest(treeView.Nodes);
451
452      bool expandUngroupedGroupNode = false;
453      var ungroupedSlaves = subResources.OfType<Slave>().OrderBy(x => x.Name);
454      if(ungroupedSlaves.Any()) {
455        ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
456          ForeColor = SystemColors.GrayText,
457          Tag = new SlaveGroup() {
458            Name = UNGROUPED_GROUP_NAME,
459            Description = UNGROUPED_GROUP_DESCRIPTION
460          }
461        };
462
463        foreach (var slave in ungroupedSlaves) {
464          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
465          if (newAssignedResources.Contains(slave)) {
466            slaveNode.Checked = true;
467            expandUngroupedGroupNode = true;
468          }
469
470          if (!HiveRoles.CheckAdminUserPermissions()) {
471            slaveNode.ForeColor = SystemColors.GrayText;
472            slaveNode.Text += IMMUTABLE_TAG;
473          } else {
474            if (addedAssignments.Contains(slave)) {
475              slaveNode.BackColor = addedAssignmentColor;
476              slaveNode.ForeColor = SystemColors.ControlText;
477              slaveNode.Text += ADDED_ASSIGNMENT_TAG;
478            } else if (removedAssignments.Contains(slave)) {
479              slaveNode.BackColor = removedAssignmentColor;
480              slaveNode.ForeColor = SystemColors.ControlText;
481              slaveNode.Text += REMOVED_ASSIGNMENT_TAG;
482              expandUngroupedGroupNode = true;
483            }
484          }
485          ungroupedGroupNode.Nodes.Add(slaveNode);
486        }
487
488        if(expandUngroupedGroupNode) ungroupedGroupNode.Expand();
489        treeView.Nodes.Add(ungroupedGroupNode);
490      } else if (ungroupedGroupNode != null) {
491        ungroupedGroupNode.Nodes.Clear();
492      }
493
494      treeView.BeforeCheck += treeView_BeforeCheck;
495      treeView.AfterCheck += treeView_AfterCheck;         
496
497      return top;
498    }
499
500    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
501      foreach (TreeNode n in nodes) {
502        Resource r = (Resource)n.Tag;
503        if (n.Nodes.Count > 0) {
504          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
505            n.Expand();
506            ExpandResourceNodesOfInterest(n.Nodes);
507          } else {
508            n.Collapse();
509          }
510        } else {
511          n.Collapse();
512        }
513      }
514    }
515
516    private bool IsAdmin() {
517      return HiveRoles.CheckAdminUserPermissions();
518    }
519
520    #endregion
521  }
522}
Note: See TracBrowser for help on using the repository browser.