/// <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; }
public FeatureDependencyUC(CMSystemDto cmSystem, CMFeatureDto cmFeature, CMTaskDto cmTask) { InitializeComponent(); this.cmSystem = cmSystem; this.cmFeature = cmFeature; this.cmTask = cmTask; }
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; }
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(); }
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); }
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); }
/// <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);
/// <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); } }
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; } } } }
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); } }