Esempio n. 1
0
        public Task <GraphNode <Key, FormNode> > Handle(GraphNode <Key, FormNode> node)
        {
            var nodeAsRouter = node.AssertType <SubTaskRouterFormNode>();

            var subTaskItems = node.Neighbors.Where(GraphNodePredicates.IsSubTaskItemRouterNode);

            var preSubTaskNode = node.Neighbors.First(GraphNodePredicates.IsPreSubTaskNode);

            var stopKey = Key.ForPostSubTaskPage(nodeAsRouter.TaskId, nodeAsRouter.SubTaskId, nodeAsRouter.RepeatIndices);

            var hasUnansweredItems = false;
            var hasAnsweredItems   = false;

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, false)
                            .WithOnNodeDequeuedHandler((props, output) =>
            {
                if (props.Node.Key.Equals(stopKey))
                {
                    output.Skip = true;
                    return;
                }

                if (props.Node.Value.IsDecisionNode)
                {
                    return;
                }

                if (!props.Node.IsTaskQuestionPageNode(out var questionNode))
                {
                    return;
                }

                if (questionNode.PageStatus == PageStatus.Unanswered)
                {
                    hasUnansweredItems = true;
                    output.IsComplete  = true;
                    return;
                }

                hasAnsweredItems = true;
            }).Build();

            foreach (var subTask in subTaskItems)
            {
                hasUnansweredItems = false;
                hasAnsweredItems   = false;

                traversal.Run(subTask);

                if (hasUnansweredItems)
                {
                    return(Task.FromResult(hasAnsweredItems ? subTask : preSubTaskNode));
                }
            }

            return(Task.FromResult(node.Neighbors.First(GraphNodePredicates.IsPostSubTaskNode)));
        }
Esempio n. 2
0
        private bool IsTaskComplete(GraphNode <Key, FormNode> taskRouter)
        {
            taskRouter.AssertType <TaskRouterFormNode>();

            // We assume all of our pages are complete and traverse each task item until we find a page which is incomplete
            var hasUnansweredItem = false;

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, false)
                            .WithOnNodeDequeuedHandler((node, output) =>
            {
                if (!node.Node.IsTaskQuestionPageNode(out var taskQuestionPageFormNode))
                {
                    return;
                }
                if (taskQuestionPageFormNode.PageStatus == PageStatus.Answered)
                {
                    return;
                }

                // If we found an unanswered page then we're finished
                hasUnansweredItem = true;
                output.IsComplete = true;
            })
                            .WithOnNodeDiscoveredHandler((props, output) =>
            {
                // We don't need to queue past the summary
                if (props.Node.IsAnyTaskSummaryNode())
                {
                    output.ShouldQueue = false;
                }
            })
                            .Build();

            var taskItemRouters = taskRouter.Neighbors.Where(GraphNodePredicates.IsTaskItemRouterNode);

            foreach (var taskItem in taskItemRouters)
            {
                traversal.Run(taskItem);
                if (hasUnansweredItem)
                {
                    break;
                }
            }

            return(!hasUnansweredItem);
        }
        public async Task TraversalTest()
        {
            var repo     = new Mock <IFormRepository>();
            var provider = new Mock <IStaticFormProvider>();
            var service  = new FormService(repo.Object, provider.Object);
            var form     = await service.InitialiseForm("test", FormType.Test);

            var           taskNodeToDebug = form.Nodes.FindByKey(Key.ForDecisionTaskRouter("last-task"));
            List <string> relationships   = new List <string>();

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, true)
                            .WithOnNodeDequeuedHandler((props, o) =>
            {
                if (props.Node.Value is TaskListFormNode)
                {
                    o.Skip = true;
                }
            })
                            .WithOnEdgeDiscoveredHandler((props, o) =>
            {
                if (props.Cost == 1)
                {
                    relationships.Add($"{GetFriendlyNodeNameForType(props.FromNode.Value)} {GetFriendlyNodeNameForType(props.ToNode.Value)}");
                }

                if (props.Cost > 1)
                {
                    o.ShouldQueue = false;
                }
            })
                            .Build();

            var result = traversal.Run(taskNodeToDebug);

            var builder = new StringBuilder();

            foreach (var r in relationships)
            {
                builder.Append(r);
                builder.AppendLine();
            }

            var output = builder.ToString();
        }
Esempio n. 4
0
        public async Task <string> DeleteSubTaskItem(FormType formType, string formKey, string subTaskNode, string subTaskItemNode)
        {
            var form = await _formRepository.GetForm(formKey);

            var subTaskRouterNode = form.Nodes.FindByKey(new Key(subTaskNode));
            var subTaskRouter     = subTaskRouterNode.AssertType <SubTaskRouterFormNode>();

            var subTaskItemRouterNode = subTaskRouterNode.Neighbors.FindByKey(new Key(subTaskItemNode));
            var subTaskItemRouter     = subTaskItemRouterNode.AssertType <SubTaskItemRouterFormNode>();

            var postSubTaskNode = subTaskRouterNode.Neighbors.FindByKey(Key.ForPostSubTaskPage(subTaskRouter.TaskId, subTaskRouter.SubTaskId, subTaskRouter.RepeatIndices));

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, true)
                            .WithOnNodeDiscoveredHandler((props, output) =>
            {
                // We do not want the PostSubTaskNode for our sub task or anything beyond it to be deleted so we prevent queueing it
                if (props.Node.Key.Equals(postSubTaskNode.Key))
                {
                    output.ShouldQueue = false;
                }
            })
                            .Build();

            var traversalResult = traversal.Run(subTaskItemRouterNode);

            foreach (var(traversedNode, _) in traversalResult.TraversalOrder)
            {
                form.Remove(traversedNode);
            }

            subTaskRouter.RemoveSubTaskItem(subTaskItemRouter.RepeatIndex);

            var nextNodeId = postSubTaskNode.Key.Value;

            // If the last SubTask has been deleted we need to ensure an empty Item is added
            if (!subTaskRouter.TaskItemIds.Any())
            {
                var subTask        = _staticFormProvider.GetSubTask(formType, StaticKey.ForSubTask(subTaskRouter.TaskId, subTaskRouter.SubTaskId));
                var nextId         = subTaskRouter.AddSubTaskItem();
                var preSubTaskNode = subTaskRouterNode.Neighbors.First(x => x.Value is PreSubTaskFormNode);
                var newNodes       = AddSubTaskItem(form, subTaskRouter.TaskId, subTask, subTaskRouter.RepeatIndices, nextId, subTaskRouterNode, preSubTaskNode, postSubTaskNode);

                // Add any required edges for new nodes
                foreach (var(_, (node, nextKey)) in newNodes)
                {
                    if (nextKey is null)
                    {
                        continue;
                    }
                    if (nextKey.Value == Key.EmptyKey().Value)
                    {
                        continue;
                    }
                    var(nextNode, _) = newNodes[nextKey];
                    form.AddDirectedEdge(node, nextNode);
                }

                // Navigate the user to the new sub task item instead of the PostSubTask page
                nextNodeId = preSubTaskNode.Key.Value;
            }

            await _formRepository.SaveForm(formKey, form);

            return(nextNodeId);
        }
Esempio n. 5
0
        public async Task <string> DeleteTaskItem(FormType formType, string formKey, string taskNodeId, string taskItemNodeId)
        {
            var form = await _formRepository.GetForm(formKey);

            var taskRouterNode = form.Nodes.FindByKey(new Key(taskNodeId));
            var taskRouter     = taskRouterNode.AssertType <TaskRouterFormNode>();

            var taskItemRouterNode = taskRouterNode.Neighbors.FindByKey(new Key(taskItemNodeId));
            var taskItemRouter     = taskItemRouterNode.AssertType <TaskItemRouterFormNode>();

            var taskItemSummaryNodeKey = Key.ForTaskSummary(taskItemRouter.TaskId, taskItemRouter.RepeatIndex);
            var postTaskNode           = taskRouterNode.Neighbors.FindByKey(Key.ForPostTaskPage(taskItemRouter.TaskId));

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, true)
                            .WithOnNodeDequeuedHandler((props, output) =>
            {
                // Anything beyond the summary we do not need to delete so we skip visiting the node
                if (props.Node.Key.Equals(taskItemSummaryNodeKey))
                {
                    output.Skip = true;
                }
            })
                            .WithOnNodeDiscoveredHandler((props, output) =>
            {
                // Prevent queuing the PostTaskNode as we do not want it included in the traversal
                if (props.Node.Key.Equals(postTaskNode.Key))
                {
                    output.ShouldQueue = false;
                }
            })
                            .Build();

            var traversalResult = traversal.Run(taskItemRouterNode);

            foreach (var(traversedNode, _) in traversalResult.TraversalOrder)
            {
                form.Remove(traversedNode);
            }

            taskRouter.RemoveTaskItem(taskItemRouter.RepeatIndex);

            var nextNodeId = postTaskNode.Key.Value;

            // If the last Task has been deleted we need to ensure an empty Item is added
            if (!taskRouter.TaskItemIds.Any())
            {
                var task        = _staticFormProvider.GetTask(formType, StaticKey.ForTaskNode(taskRouter.TaskId));
                var nextId      = taskRouter.AddTaskItem();
                var preTaskNode = taskRouterNode.Neighbors.First(x => x.Value is PreTaskFormNode || x.Value is PreTaskGhost);
                var newNodes    = AddTaskItem(form, taskRouterNode, preTaskNode, postTaskNode, task, nextId);

                // Add any required edges for new nodes
                foreach (var(_, (node, nextKey)) in newNodes)
                {
                    if (nextKey is null)
                    {
                        continue;
                    }
                    if (nextKey.Value == Key.EmptyKey().Value)
                    {
                        continue;
                    }
                    var(nextNode, _) = newNodes[nextKey];
                    form.AddDirectedEdge(node, nextNode);
                }

                // Navigate the user to the new task item instead of the PostTask page
                nextNodeId = preTaskNode.Key.Value;
            }

            await _formRepository.SaveForm(formKey, form);

            return(nextNodeId);
        }
        public Task <GraphNode <Key, FormNode> > Handle(GraphNode <Key, FormNode> node)
        {
            node.AssertType <TaskRouterFormNode>();

            var preTaskNode  = node.Neighbors.First(GraphNodePredicates.IsAnyPreTaskNode);
            var postTaskNode = node.Neighbors.First(GraphNodePredicates.IsAnyPostTaskNode);

            var taskItemRouters = node.Neighbors.Where(GraphNodePredicates.IsTaskItemRouterNode)
                                  .OrderBy(x => x.IsTaskItemRouterNode(out var taskItem) ? taskItem.RepeatIndex : 0).ToList();

            var hasAnsweredItems = false;
            GraphNode <Key, FormNode> summaryNode = null;

            var traversal = new GraphTraversalBuilder <Key, FormNode>(TraversalType.BreadthFirstSearch, true)
                            .WithOnNodeDequeuedHandler((props, output) =>
            {
                if (props.Node.Value.IsDecisionNode)
                {
                    return;
                }

                if (props.Node.IsAnyTaskSummaryNode())
                {
                    summaryNode = props.Node;
                    output.Skip = true;
                    return;
                }


                if (!props.Node.IsTaskQuestionPageNode(out var taskPageNode))
                {
                    return;
                }

                if (taskPageNode.PageStatus == PageStatus.Unanswered)
                {
                    output.IsComplete = true;
                    return;
                }

                hasAnsweredItems = true;
            })
                            .WithOnEdgeDiscoveredHandler((props, output) =>
            {
                if (props.Cost == 1)
                {
                    return;
                }

                if (props.Cost == EdgeCosts.SubTaskToPostSubTaskCost)
                {
                    if (props.FromNode.IsSubTaskRouterNode() && props.ToNode.IsPostSubTaskNode())
                    {
                        output.ShouldQueue = false;
                    }
                }
            })
                            .Build();



            // Run the traversal on each of our TaskItems
            foreach (var router in taskItemRouters)
            {
                hasAnsweredItems = false;
                summaryNode      = null;

                var result = traversal.Run(router);

                // Found an unanswered node
                if (result.Result == null)
                {
                    continue;
                }

                // We had no answered questions and only one repeat (the user has never visited the task)
                if (!hasAnsweredItems && taskItemRouters.Count == 1)
                {
                    return(Task.FromResult(preTaskNode));
                }

                // The user has answered some questions but not all
                return(Task.FromResult(router));
            }

            // All items were answered
            return(postTaskNode.Value.IsDecisionNode ? Task.FromResult(summaryNode) : Task.FromResult(postTaskNode));
        }