/// <summary>
        /// Gets the user control meant to edit or view a particular task
        /// </summary>
        /// <param name="cmTaskType"></param>
        /// <param name="cmSystem"></param>
        /// <param name="cmFeature"></param>
        /// <param name="cmTask"></param>
        /// <returns></returns>
        public UserControl GetTaskUI(CMTaskTypeDto cmTaskType, CMSystemDto cmSystem, CMFeatureDto cmFeature, CMTaskDto cmTask)
        {
            var taskFactory = GetTaskFactory(cmTaskType.Name);
            var taskUC      = taskFactory.GetTaskUI(cmTaskType, cmSystem, cmFeature, cmTask);

            return(taskUC);
        }
        private TreeViewItem GetTVI_Feature(CMFeatureDto cmFeature)
        {
            var cmFeatureTVI = new TreeViewItem()
            {
                Header = cmFeature.Name,
                Tag    = new TreeViewTag(cmFeature)
            };

            var contextMenu = new ContextMenu();

            cmFeatureTVI.ContextMenu = contextMenu;

            var openFeatureMenu = new MenuItem()
            {
                Header = "Open Feature"
            };

            contextMenu.Items.Add(openFeatureMenu);
            openFeatureMenu.Click += (sender, e) =>
            {
                new FeatureEditor(cmFeature).Show();
            };

            return(cmFeatureTVI);
        }
        /// <summary>
        /// Call this when a feature is updated, it will determine if any dependency should be moved to the closed state.
        /// Also moves out of the closed state if the dependant feature moves away from its target state.
        /// </summary>
        /// <param name="featureBeforeDto"></param>
        /// <param name="featureAfterDto"></param>
        internal static void UpdateTaskStatesForFeatureDependendies(CMFeatureDto featureBeforeDto, CMFeatureDto featureAfterDto)
        {
            // FeatureAfterDto may be null during a delete, use the before version for the Id and template check, later use the after one if it is not null

            // Don't process updates if the feature isn't real yet.
            if (featureBeforeDto.Id == 0)
            {
                return;
            }

            // Don't process updates for feature templates
            if (featureBeforeDto.IsTemplate)
            {
                return;
            }

            // Look for dependency task data that reference this feature that is being changed
            var linkedTaskDatas = FeatureDependencyDataProvider.GetAll_ForInstancedFeatureId(featureBeforeDto.Id);

            // If there are no dependencies on the feature that was updated, then there is nothing more to do.
            if (!linkedTaskDatas.Any())
            {
                return;
            }

            // For each dependency data that was watching this feature
            foreach (var linkedTaskData in linkedTaskDatas)
            {
                UpdateTaskStatesForFeatureDependendies(linkedTaskData, featureAfterDto);
            }
        }
        /// <summary>
        /// Resolves all of the feature vars currently possible for a feature
        /// </summary>
        /// <param name="feature"></param>
        public static void ResolveFeatureVarsForFeatureAndTasks(this CMFeatureDto feature)
        {
            // Nothing should be resolving feature vars for a feature template
            if (feature.IsTemplate)
            {
                throw new InvalidOperationException("Resolving feature vars for a feature template is not supported.");
            }

            var featureVars = CMDataProvider.DataStore.Value.CMFeatureVarStrings.Value.GetAll_ForFeature(feature.Id).ToList();

            // Currently the only thing that can use feature vars in a feature is the name.
            string newFeatureName = ResolveFeatureVarsInString(feature.Name, featureVars);

            // Only update the feature if an update was made
            if (!newFeatureName.Equals(feature.Name, StringComparison.OrdinalIgnoreCase))
            {
                feature.Name = newFeatureName;

                var opFeatureUpdate = CMDataProvider.DataStore.Value.CMFeatures.Value.Update(feature);
                if (opFeatureUpdate.Errors.Any())
                {
                    throw new Exception(opFeatureUpdate.ErrorsCombined);
                }
            }

            // Update the title on all tasks (task data updates are handled fully by the task factory catalog CUD callbacks)
            var tasks = CMDataProvider.DataStore.Value.CMTasks.Value.GetAll_ForFeature(feature.Id);

            foreach (var cmTask in tasks)
            {
                cmTask.ResolveFeatureVars(featureVars);
            }
        }
        public FeatureCreator(CMFeatureDto featureTemplate)
        {
            InitializeComponent();
            this.FeatureTemplate = featureTemplate;

            txtFeatureName.Text = featureTemplate.Name;
        }
Exemple #6
0
        public FeatureDependencyUC(CMSystemDto cmSystem, CMFeatureDto cmFeature, CMTaskDto cmTask)
        {
            InitializeComponent();

            this.cmSystem  = cmSystem;
            this.cmFeature = cmFeature;
            this.cmTask    = cmTask;
        }
Exemple #7
0
        public FeatureEditorUC(CMFeatureDto cmFeature, Window parentWindow)
        {
            InitializeComponent();
            this.cmFeatureDto = cmFeature;
            this.parentWindow = parentWindow;

            // Updating a task reloads the task list if the task state changes
            CMDataProvider.DataStore.Value.CMTasks.Value.OnRecordUpdated += TaskRecord_Updated_ReloadTaskList;
        }
Exemple #8
0
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Title = cmTaskDto.Title;

            ref_TaskTypeDto = CMDataProvider.DataStore.Value.CMTaskTypes.Value.Get(cmTaskDto.CMTaskTypeId);
            ref_FeatureDto  = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(cmTaskDto.CMFeatureId);
            ref_SystemDto   = CMDataProvider.DataStore.Value.CMSystems.Value.Get(ref_FeatureDto.CMSystemId);

            LoadTaskStatesCombo();
            LoadTaskUI();
        }
Exemple #9
0
        private void txtFeatureDescription_LostFocus(object sender, RoutedEventArgs e)
        {
            var opResult = CMDataProvider.DataStore.Value.CMFeatures.Value.UpdateIfNeeded_Description(cmFeatureDto.Id, txtFeatureDescription.Text);

            if (opResult.Errors.Any())
            {
                MessageBox.Show(opResult.ErrorsCombined);
                txtFeatureDescription.Text = cmFeatureDto.Description;
                return;
            }

            cmFeatureDto = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(cmFeatureDto.Id);
        }
Exemple #10
0
        private void txtFeatureName_LostFocus(object sender, RoutedEventArgs e)
        {
            var opResult = CMDataProvider.DataStore.Value.CMFeatures.Value.UpdateIfNeeded_Name(cmFeatureDto.Id, txtFeatureName.Text);

            if (opResult.Errors.Any())
            {
                MessageBox.Show(opResult.ErrorsCombined);
                txtFeatureName.Text = cmFeatureDto.Name;
                return;
            }

            cmFeatureDto = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(cmFeatureDto.Id);
            if (parentWindow != null)
            {
                parentWindow.Title = cmFeatureDto.Name;
            }
        }
        private TreeViewItem GetTVI_FeatureTemplate(CMFeatureDto cmFeatureTemplate)
        {
            var cmFeatureTemplateTreeViewItem = new TreeViewItem()
            {
                Header = cmFeatureTemplate.Name,
                Tag    = new TreeViewTag(cmFeatureTemplate)
            };

            var contextMenu = new ContextMenu();

            var deleteFeatureTemplateMenu = new MenuItem()
            {
                Header = "Delete Feature Template"
            };

            contextMenu.Items.Add(deleteFeatureTemplateMenu);
            deleteFeatureTemplateMenu.Click += (sender, e) =>
            {
                var selectedTreeViewTag = treeConfig.GetSelectedTreeViewTag();

                if (selectedTreeViewTag?.Dto == null || !(selectedTreeViewTag?.Dto is CMFeatureDto))
                {
                    return;
                }

                var selectedFeatureTemplateDto = selectedTreeViewTag.Dto as CMFeatureDto;

                var opResult = CMDataProvider.DataStore.Value.CMFeatures.Value.Delete(selectedFeatureTemplateDto.Id);
                if (opResult.Errors.Any())
                {
                    MessageBox.Show(opResult.ErrorsCombined);
                    return;
                }
            };
            cmFeatureTemplateTreeViewItem.ContextMenu = contextMenu;

            return(cmFeatureTemplateTreeViewItem);
        }
Exemple #12
0
 /// <summary>
 /// The UI that is shown when editing a task
 /// </summary>
 /// <param name="cmTaskType"></param>
 /// <param name="cmSystem"></param>
 /// <param name="cmFeature"></param>
 /// <param name="cmTask"></param>
 /// <returns></returns>
 public abstract UserControl GetTaskUI(CMTaskTypeDto cmTaskType, CMSystemDto cmSystem, CMFeatureDto cmFeature, CMTaskDto cmTask);
Exemple #13
0
        /// <summary>
        /// Creates an instance of a feature template.
        /// </summary>
        /// <param name="featureTemplate"></param>
        /// <returns></returns>
        public static CMFeatureDto ToInstance(this CMFeatureDto featureTemplate, List <CMFeatureVarStringDto> initialFeatureVars)
        {
            if (!featureTemplate.IsTemplate)
            {
                throw new NotImplementedException("Instancing a feature that is already an instance is not implemented.");
            }

            // Clone the feature template to a feature instance
            var featureInstanceDto = new CMFeatureDto()
            {
                CMParentFeatureTemplateId = featureTemplate.Id,
                CMSystemId           = featureTemplate.CMSystemId,
                Name                 = featureTemplate.Name,
                TasksBackgroundColor = featureTemplate.TasksBackgroundColor
            };

            // Calculate a default feature state, which is needed to insert into the db
            // Due to the fact that the feature has no cloned tasks yet, this state is likely temporary and will be updated again below
            featureInstanceDto.RecalculateSystemState();

            var opFeatureInsert = CMDataProvider.DataStore.Value.CMFeatures.Value.Insert(featureInstanceDto);

            if (opFeatureInsert.Errors.Any())
            {
                throw new Exception(opFeatureInsert.ErrorsCombined);
            }

            // The new feature has an Id now, we can set up the feature vars specific to this feature
            initialFeatureVars.Add(
                new CMFeatureVarStringDto()
            {
                Name  = "Feature.Id",
                Value = featureInstanceDto.Id.ToString()
            });

            // Add all feature vars to the feature
            // This will trigger other routines that resolve the feature vars within tasks and features to execute in response through the CUD events
            foreach (var featureVar in initialFeatureVars)
            {
                // Attach the feature var to the new feature
                featureVar.CMFeatureId = featureInstanceDto.Id;

                var opFeatureVarStringInsert = CMDataProvider.DataStore.Value.CMFeatureVarStrings.Value.Insert(featureVar);
                if (opFeatureVarStringInsert.Errors.Any())
                {
                    throw new Exception(opFeatureVarStringInsert.ErrorsCombined);
                }
            }

            // Instances each task in the feature template
            var taskTemplates = CMDataProvider.DataStore.Value.CMTasks.Value.GetAll_ForFeature(featureTemplate.Id);

            foreach (var cmTaskTemplate in taskTemplates)
            {
                var taskInstance = cmTaskTemplate.ToInstance(featureInstanceDto.Id);

                var opTaskInsert = CMDataProvider.DataStore.Value.CMTasks.Value.Insert(taskInstance);
                if (opTaskInsert.Errors.Any())
                {
                    throw new Exception(opTaskInsert.ErrorsCombined);
                }

                // For the task data we revert to the task factory to provide it
                var taskTemplateType = CMDataProvider.DataStore.Value.CMTaskTypes.Value.Get(cmTaskTemplate.CMTaskTypeId);
                TaskFactoriesCatalog.Instance.CreateTaskDataInstance(taskTemplateType, cmTaskTemplate, taskInstance);
            }

            // Recalculate the current system state again after the feature has tasks assigned
            featureInstanceDto.RecalculateSystemState();

            return(featureInstanceDto);
        }
        private TreeViewItem GetTVI_System(CMSystemDto cmSystem)
        {
            var cmSystemTreeViewItem = new TreeViewItem()
            {
                Header = cmSystem.Name,
                Tag    = new TreeViewTag(cmSystem)
            };

            var contextMenu = new ContextMenu();

            var addNewFeatureTemplate = new MenuItem()
            {
                Header = "Add New Feature Template"
            };

            contextMenu.Items.Add(addNewFeatureTemplate);
            addNewFeatureTemplate.Click += (sender, e) =>
            {
                var newCMFeatureTemplate = new CMFeatureDto()
                {
                    Name       = "New Feature Template",
                    CMSystemId = cmSystem.Id
                };

                var opResult = CMDataProvider.DataStore.Value.CMFeatures.Value.Insert(newCMFeatureTemplate);
                if (opResult.Errors.Any())
                {
                    MessageBox.Show(opResult.ErrorsCombined);
                    return;
                }

                // Add a set of default transition rules that match the current system states
                int rulePriority = 1;
                var systemStates = CMDataProvider.DataStore.Value.CMSystemStates.Value.GetAll_ForSystem(cmSystem.Id)
                                   .OrderBy(s => s.MigrationOrder) // Default the order to the same as the system states migration
                                   .ToList();
                // Add all rules from the system in the same order as the system. The user can delete the unwanted ones and re-order them
                foreach (var currentState in systemStates)
                {
                    var newTransitionRule = new CMFeatureStateTransitionRuleDto()
                    {
                        CMFeatureId     = newCMFeatureTemplate.Id,
                        Order           = rulePriority++,
                        CMSystemStateId = currentState.Id
                    };
                    var opResultRule = CMDataProvider.DataStore.Value.CMFeatureStateTransitionRules.Value.Insert(newTransitionRule);
                    if (opResultRule.Errors.Any())
                    {
                        MessageBox.Show(opResultRule.ErrorsCombined);
                        return;
                    }
                }

                if (systemStates.Any())
                {
                    MessageBox.Show("Feature state transition rules were automatically added. Please validate these rules.");
                }

                var featureTemplateTVI = GetTVI_FeatureTemplate(newCMFeatureTemplate);
                cmSystemTreeViewItem.Items.Add(featureTemplateTVI);
            };

            var deleteSystemMenu = new MenuItem()
            {
                Header = "Delete System"
            };

            contextMenu.Items.Add(deleteSystemMenu);
            deleteSystemMenu.Click += (sender, e) =>
            {
                var selectedTreeViewTag = treeConfig.GetSelectedTreeViewTag();

                if (selectedTreeViewTag?.Dto == null || !(selectedTreeViewTag?.Dto is CMSystemDto))
                {
                    return;
                }

                // mcbtodo: Add confirm dialogs on delete operations where appropriate
                var selectedCMSystemDto = selectedTreeViewTag.Dto as CMSystemDto;

                var opResult = CMDataProvider.DataStore.Value.CMSystems.Value.Delete(selectedCMSystemDto.Id);
                if (opResult.Errors.Any())
                {
                    MessageBox.Show(opResult.ErrorsCombined);
                    return;
                }
            };

            cmSystemTreeViewItem.ContextMenu = contextMenu;

            return(cmSystemTreeViewItem);
        }
        internal static void UpdateTaskStatesForFeatureDependendies(FeatureDependencyDto linkedTaskData, CMFeatureDto trackedFeature)
        {
            var cmTaskInstance = CMDataProvider.DataStore.Value.CMTasks.Value.Get(linkedTaskData.TaskId);

            if (cmTaskInstance == null)
            {
                // If the task this data links to is null then somehow the task was deleted without deleting the data,
                // Do so now
                FeatureDependencyDataProvider.Delete(linkedTaskData.Id);
                return;
            }

            if (trackedFeature == null && linkedTaskData.InstancedCMFeatureId != 0)
            {
                trackedFeature = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(linkedTaskData.InstancedCMFeatureId);
            }

            // Figure out what the task (that is associated with the dependency data) state should be
            CMTaskStateDto shouldBeState = null;

            if (linkedTaskData.PathOptionChosen == false)
            {
                shouldBeState = FeatureDependency_TaskState_WaitingOnChoice;
            }
            else
            {
                shouldBeState = trackedFeature == null || linkedTaskData.InstancedTargetCMSystemStateId == trackedFeature.CMSystemStateId ?
                                FeatureDependency_TaskState_Closed :
                                FeatureDependency_TaskState_WaitingOnDependency;
            }

            // Now check to see if the dependency task actually is in that state
            if (cmTaskInstance.CMTaskStateId != shouldBeState.Id)
            {
                // If it's not in the state it should be, then do the update so it is.
                // All of the checks to avoid doing an update are to avoid chain reactions with the CUD events
                cmTaskInstance.CMTaskStateId = shouldBeState.Id;
                CMDataProvider.DataStore.Value.CMTasks.Value.Update(cmTaskInstance);
            }
        }
Exemple #16
0
        private void ReloadUI()
        {
            TaskData = FeatureDependencyExtensions.FeatureDependencyDataProvider.Get_ForTaskId(cmTask.Id);

            if (TaskData == null)
            {
                TaskData = new FeatureDependencyDto()
                {
                    TaskId = cmTask.Id
                };
            }

            dataGridEditFeatureDependencySettings.Visibility = Visibility.Hidden;
            dataGridChooseFeatureDependency.Visibility       = Visibility.Hidden;
            gridChosen.Visibility = Visibility.Hidden;

            dataGridEditFeatureDependencySettings.ItemsSource = null;
            dataGridChooseFeatureDependency.ItemsSource       = null;

            // Display as a task template
            if (cmTask.IsTemplate)
            {
                dataGridEditFeatureDependencySettings.Visibility = Visibility.Visible;

                dataGridEditFeatureDependencySettings.ItemsSource = TaskData.PathOptions;
            }
            // Display as a task instance
            else
            {
                // If choice has been made and there are no options to choose from then jump straight to the
                // dialog to select a dependency.
                if (TaskData.PathOptionChosen == false && !TaskData.PathOptions.Any())
                {
                    var featureSelectorUC = ShowFeatureSelectorWindow(0, 0, cmTask.IsTemplate);
                    if (featureSelectorUC.SelectionConfirmed)
                    {
                        TaskData.PathOptionChosen               = true;
                        TaskData.InstancedCMFeatureId           = featureSelectorUC.SelectedFeatureId;
                        TaskData.InstancedTargetCMSystemStateId = featureSelectorUC.SelectedSystemStateId;
                        UpdateTaskData();
                    }
                }

                // Display for an instance that does not yet have the choice made
                if (TaskData.PathOptionChosen == false)
                {
                    dataGridChooseFeatureDependency.Visibility  = Visibility.Visible;
                    dataGridChooseFeatureDependency.ItemsSource = TaskData.PathOptions;
                }
                // Display for an instance that already has the choice made
                else
                {
                    gridChosen.Visibility = Visibility.Visible;

                    if (TaskData.InstancedCMFeatureId == 0)
                    {
                        txtChosenFeatureName.Text = "<None>";
                        txtChosenTargetState.Text = "<None>";
                    }
                    else
                    {
                        cmTargetFeature = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(TaskData.InstancedCMFeatureId);
                        var cmTargetSystemState = CMDataProvider.DataStore.Value.CMSystemStates.Value.Get(TaskData.InstancedTargetCMSystemStateId);
                        txtChosenFeatureName.Text = cmTargetFeature.Name;
                        txtChosenTargetState.Text = cmTargetSystemState.Name;
                    }
                }
            }
        }
Exemple #17
0
        public override UserControl GetTaskUI(CMTaskTypeDto cmTaskType, CMSystemDto cmSystem, CMFeatureDto cmFeature, CMTaskDto cmTask)
        {
            switch (cmTaskType.Name)
            {
            case nameof(BuildInTaskTypes.FeatureDependency):
                var featureDependencyTaskUI = new FeatureDependencyUC(cmSystem, cmFeature, cmTask);
                return(featureDependencyTaskUI);

            case nameof(BuildInTaskTypes.Note):
                var noteTaskUI = new NoteUC(cmSystem, cmFeature, cmTask);
                return(noteTaskUI);
            }

            return(null);
        }
 public FeatureEditor(CMFeatureDto cmFeatureDto)
 {
     this.cmFeatureDto = cmFeatureDto;
     InitializeComponent();
 }
        /// <summary>
        /// Processes the state transition rules for the feature and sets the new system state if appropriate.
        /// </summary>
        /// <param name="cmFeature"></param>
        /// <returns>
        /// Returns true if the state was changed.
        /// If the feature is already in the db it will be updated there as well.
        /// If the feature has not yet been inserted, only the object passed in will contain the updated state.
        /// </returns>
        public static bool RecalculateSystemState(this CMFeatureDto cmFeature)
        {
            // State is not calculated for feature templates
            if (cmFeature.CMParentFeatureTemplateId == 0)
            {
                return(false);
            }

            // Refresh the lookups if needed
            RefreshLookups();

            var transitionRules = transitionRulesByFeatureId.ContainsKey(cmFeature.CMParentFeatureTemplateId) ?
                                  transitionRulesByFeatureId[cmFeature.CMParentFeatureTemplateId] :
                                  new List <CMFeatureStateTransitionRuleDto>();

            if (!transitionRules.Any())
            {
                throw new Exception("Unable to set a default system state on the feature because no state transition rules are set up yet for this feature type.");
            }

            // If the logic falls through the loop below and doesn't match anything, default to the last state listed
            int shouldBeSystemStateId = transitionRules.Last().CMSystemStateId;

            foreach (var transitionRule in transitionRules)
            {
                var tasksInReferencedSystemState = tasksByFeatureId.ContainsKey(cmFeature.Id) ?
                                                   tasksByFeatureId[cmFeature.Id].Where(t => t.CMSystemStateId == transitionRule.CMSystemStateId) :
                                                   new List <CMTaskDto>();

                // A feature with no tasks is the same as a feature that has all of it's tasks closed. As far as this logic goes.
                // Same idea for: A system state (within a feature) that has no tasks considers that system state to have all of it's tasks closed.
                // Therefore when creating a new feature, before the tasks are added, the feature will default to the lowest priority state.
                if (tasksInReferencedSystemState.Any(cmTask =>
                {
                    var closedTaskStateId = closedTaskStateByTaskTypeId[cmTask.CMTaskTypeId];
                    return(cmTask.CMTaskStateId != closedTaskStateId);
                }))
                {
                    shouldBeSystemStateId = transitionRule.CMSystemStateId;
                    break; // Stop looping through the rules as soon as we find a state that has unclosed tasks
                }
            }

            // mcbtodo: see if there is an easy way that works to wrap this checking and update logic into extension methods. It would handle updating *just* the system state id and keeping whatever title is currently in the db.
            if (cmFeature.CMSystemStateId != shouldBeSystemStateId)
            {
                cmFeature.CMSystemStateId = shouldBeSystemStateId;
                if (cmFeature.Id != 0)
                {
                    // Refresh the feature data before we check as a callback on another thread may have updated it
                    cmFeature = CMDataProvider.DataStore.Value.CMFeatures.Value.Get(cmFeature.Id);
                    cmFeature.CMSystemStateId = shouldBeSystemStateId;

                    var opResult = CMDataProvider.DataStore.Value.CMFeatures.Value.Update(cmFeature);
                    if (opResult.Errors.Any())
                    {
                        throw new Exception(opResult.ErrorsCombined);
                    }
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }