Commit 8c511532 authored by Bas Lijnse's avatar Bas Lijnse

Moved debug info to services

git-svn-id: https://svn.cs.ru.nl/repos/iTask-system/trunk@1122 63da3aa8-80fd-4f01-9db8-e6ea747a3da2
parent 550328ea
......@@ -53,8 +53,6 @@
,{"text":"itasks.DebugPanel.js","path":"src/js/"}
,{"text":"itasks.WorkListPanel.js","path":"src/js/"}
,{"text":"itasks.HomeTabPanel.js","path":"src/js/"}
,{"text":"itasks.TaskForestTabPanel.js","path":"src/js/"}
,{"text":"itasks.ProcessTableTabPanel.js","path":"src/js/"}
,{"text":"itasks.WorkTabPanel.js","path":"src/js/"}
,{"text":"itasks.ResultTabPanel.js","path":"src/js/"}
,{"text":"itasks.WorkTabsPanel.js","path":"src/js/"}
......
......@@ -14,36 +14,6 @@ itasks.DebugPanel = Ext.extend(Ext.Panel, {
bodyStyle: "padding: 5px",
deferredRender: false,
items: [{
xtype: "fieldset",
title: "Overviews",
autoHeight: true,
items: [{
xtype: "button",
text: "Show task forest...",
iconCls: "icon-task-tree",
style: "margin-bottom: 2px;",
listeners: {
click: {
fn: function() {
this.worktabs.openTaskForestTab();
},
scope: this
}
}
},{
xtype: "button",
text: "Show process table...",
iconCls: "icon-process-table",
listeners: {
click: {
fn: function() {
this.worktabs.openProcessTableTab();
},
scope: this
}
}
}]
},{
xtype: "fieldset",
title: "Session",
html: itasks.app.session,
......
/**
* Tab panel which shows the global process table.
*/
Ext.ns('itasks');
itasks.ProcessTableTabPanel = Ext.extend(itasks.RemoteDataPanel, {
applicationPanel: undefined,
initComponent: function () {
Ext.apply(this, {
title: "Process table",
closable: true,
autoScroll: true,
url: itasks.config.serverUrl + "/debug/processtable",
cls: "worktab-container",
iconCls: "icon-process-table",
bodyStyle: "padding: 10px;",
tbar: [{
text: "Refresh",
iconCls: "x-tbar-loading",
listeners: { click: { fn: function (btn) {this.refresh();}, scope: this}}
}]
});
itasks.ProcessTableTabPanel.superclass.initComponent.apply(this, arguments);
},
update: function(data) {
this.body.dom.innerHTML = data;
}
});
Ext.reg("itasks.processtabletab",itasks.ProcessTableTabPanel);
\ No newline at end of file
/**
* Tab panel which shows the global task forest.
*/
Ext.ns("itasks");
itasks.TaskForestTabPanel = Ext.extend( Ext.Panel, {
initComponent: function () {
this.tree = new Ext.ux.tree.TreeGrid({
id : 'treepanel',
region : 'center',
rootVisible: false,
autoScroll: true,
title: "Task Overview",
border: false,
bodyStyle: "background-color: white",
iconCls : "icon-task-tree",
columns:[{
header:'Task Id',
dataIndex:'taskId',
width: 500
},{
header:'Assigned to..',
dataIndex:'user',
renderer: Ext.util.Format.htmlEncode,
width: 100
},{
header:'Task Label',
dataIndex:'taskLabel',
width: 200
}],
loader: new Ext.tree.TreeLoader({
dataUrl: itasks.config.serverUrl + "/debug/taskforest",
baseParams: {_session: itasks.app.session},
requestMethod: "POST",
uiProviders: { col : Ext.ux.tree.TreeGridNodeUI}
}),
root: new Ext.tree.AsyncTreeNode({
text:'Tasks'
})
});
this.iconPanel = new Ext.Panel({
id: 'iconpanel',
region: 'south',
height: 20,
border: false,
html: '<div class="taskForestLegend task-mnt">Main Task</div>'+
'<div class="taskForestLegend task-int">Edit Task</div>'+
'<div class="taskForestLegend task-mon">Monitor Task</div>'+
'<div class="taskForestLegend task-ins">Instruction Task</div>'+
'<div class="taskForestLegend task-fin">Finished Task</div>'+
'<div class="taskForestLegend task-seq">Sequential Combinator</div>'+
'<div class="taskForestLegend task-grp">Grouping Combinator</div>'+
'<div class="taskForestLegend task-par">Parallel Combinator</div>'+
'<div class="taskForestLegend task-rpc">Remote Procedure Call</div>'+
'<div class="x-clear"></div>'
});
Ext.apply(this, {
title: "Task forest",
closable: true,
autoScroll: false,
cls: "worktab-container",
iconCls: "icon-task-tree",
layout: "border",
bodyStyle: "background-color: white",
items: [this.tree, this.iconPanel],
tbar: [{
text: "Refresh",
iconCls: "x-tbar-loading",
listeners: {click: {fn: function (btn) {this.refresh();},scope: this}}
},{
text: "Expand Tree",
iconCls: "icon-expand-tree",
listeners: {click: {fn: function (btn) {this.expandTree();},scope: this}}
},{
text: "Collapse Tree",
iconCls: "icon-collapse-tree",
listeners: {click: {fn: function (btn) {this.collapseTree();},scope: this}}
}]
});
itasks.TaskForestTabPanel.superclass.initComponent.apply(this, arguments);
},
refresh: function(){
this.tree.getRootNode().reload();
},
expandTree: function(){
//var rootNode = this.tree.getRootNode();
//var toggleChild = function(child){child.expand(true,true);};
//rootNode.eachChild(toggleChild);
this.tree.expandAll();
},
collapseTree: function(){
//var rootNode = this.tree.getRootNode();
//var toggleChild = function(child){child.collapse(true,false);};
//rootNode.eachChild(toggleChild);
this.tree.collapseAll();
}
});
Ext.reg("itasks.taskforesttab",itasks.TaskForestTabPanel);
\ No newline at end of file
......@@ -72,26 +72,6 @@ itasks.WorkTabsPanel = Ext.extend(Ext.TabPanel, {
}
this.activate(tab);
return tab;
},
openTaskForestTab: function () {
var tab = this.getComponent("taskforesttab");
if(tab == undefined) {
tab = new itasks.TaskForestTabPanel({id: "taskforesttab"});
this.add(tab);
}
this.activate(tab);
tab.refresh();
return tab;
},
openProcessTableTab: function () {
var tab = this.getComponent("processtabletab");
if(tab == undefined) {
tab = new itasks.ProcessTableTabPanel({id: "processtabletab"});
this.add(tab);
}
this.activate(tab);
tab.refresh();
return tab;
}
});
......
......@@ -108,7 +108,6 @@
<script type="text/javascript" src="../js/itasks.WorkListPanel.js"></script>
<script type="text/javascript" src="../js/itasks.HomeTabPanel.js"></script>
<script type="text/javascript" src="../js/itasks.TaskForestTabPanel.js"></script>
<script type="text/javascript" src="../js/itasks.ProcessTableTabPanel.js"></script>
<script type="text/javascript" src="../js/itasks.WorkTabPanel.js"></script>
<script type="text/javascript" src="../js/itasks.ResultTabPanel.js"></script>
<script type="text/javascript" src="../js/itasks.WorkTabsPanel.js"></script>
......
......@@ -15,10 +15,7 @@ import Http, HttpUtil
from HttpServer import :: HTTPServerControl(..), :: HTTPServerOption(..)
import Setup
//import AuthenticationHandler, DeauthenticationHandler
//import NewListHandler, NewStartHandler, WorkListHandler, UserListHandler
import WorkTabHandler, PropertyHandler
import TaskTreeForestHandler, ProcessTableHandler
import RPCHandlers
import ApplicationService, SessionService, WorkflowService, TaskService, UserService, DocumentService
......@@ -32,7 +29,7 @@ PATH_SEP :== "\\"
// The iTasks engine consist of a set of HTTP request handlers
engine :: !(Maybe Config) [Workflow] -> [(!String -> Bool, HTTPRequest *World -> (!HTTPResponse, !HTTPServerControl, !*World))]
engine mbConfig userflows
engine mbConfig userFlows
= case mbConfig of
Just config
= handlers config
......@@ -40,31 +37,21 @@ engine mbConfig userflows
= [(\_ -> True, setupHandler handlers)]
where
handlers config
= [ // 'new' services
(startsWith "/services", serviceDispatch config flows)
//'old' handlers
//,((==) (config.serverPath +++ "/authenticate"), handleAnonRequest config flows handleAuthenticationRequest)
//,((==) (config.serverPath +++ "/deauthenticate"), handleSessionRequest config flows handleDeauthenticationRequest)
//,((==) (config.serverPath +++ "/new/list"), handleSessionRequest config flows handleNewListRequest)
//,((==) (config.serverPath +++ "/new/start"), handleSessionRequest config flows handleNewStartRequest)
//,((==) (config.serverPath +++ "/work/list"), handleSessionRequest config flows handleWorkListRequest)
,((==) (config.serverPath +++ "/work/tab"), handleSessionRequest config flows handleWorkTabRequest)
= [
//'old' handlers
((==) (config.serverPath +++ "/work/tab"), handleSessionRequest config flows handleWorkTabRequest)
,((==) (config.serverPath +++ "/work/property"), handleSessionRequest config flows handlePropertyRequest)
//,((==) (config.serverPath +++ "/data/users"), handleSessionRequest config flows handleUserListRequest)
,((==) (config.serverPath +++ "/rpc/request"), handleSessionRequest config flows handleRPCListRequest)
,((==) (config.serverPath +++ "/rpc/response"), handleSessionRequest config flows handleRPCUpdates)
,((==) (config.serverPath +++ "/debug/taskforest"), handleSessionRequest config flows handleTaskForestRequest)
,((==) (config.serverPath +++ "/debug/processtable"), handleSessionRequest config flows handleProcessTableRequest)
//,((==) (config.serverPath +++ "/document/download"), handleSessionRequest config flows handleDocumentDownloadRequest)
//,((==) (config.serverPath +++ "/document/upload"), handleSessionRequest config flows handleDocumentUploadRequest)
//,((==) (config.serverPath +++ "/document/clear"), handleSessionRequest config flows handleDocumentClearRequest)
//,((startsWith) (config.serverPath +++ "/document/download/link"), handleSessionRequest config flows handleDocumentDownloadLinkRequest)
//,((startsWith) (config.serverPath +++ "/document/preview/link"), handleSessionRequest config flows handleDocumentPreviewLinkRequest)
// Webservices
,(startsWith "/services", serviceDispatch config flows)
// Handler to stop the server nicely
,((==) "/stop", handleStopRequest)
,(\_ -> True, handleStaticResourceRequest config)
]
//Always add the workflows for administering the itask system
flows = userflows ++ userAdministration
flows = userAdministration ++ userFlows
serviceDispatch config flows req world
# tst = initTSt req config flows world
......
......@@ -4,8 +4,6 @@ import HtmlUtil, Text
import JSON
import StdList
import StdMisc // abort
import ProcessDB
import TaskPanel
......@@ -14,11 +12,35 @@ derive bimap (,), Maybe
derive JSONEncode Menu, MenuItem, Hotkey, Key, Action, User, UserDetails, Password
derive JSONDecode ManagerProperties, TaskPriority, User, UserDetails, Password
//Additional derives for debugging
derive JSONEncode Process, TaskParallelType, TaskParallelInfo, TaskInfo
derive JSONEncode GroupActionsBehaviour, GroupedBehaviour
derive JSONEncode HtmlTag, HtmlAttr
//Can't derive TaskTree serialization because the damn thing contains functions
//on unique states @!#$%!!
JSONEncode{|TaskTree|} (TTMainTask a0 a1 a2 a3 a4)
= [JSONArray [JSONString "TTMainTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1 ++ JSONEncode{|*|} a2 ++ JSONEncode{|*|} a3 ++ JSONEncode{|*|} a4]]
JSONEncode{|TaskTree|} (TTInteractiveTask a0 a1)
= [JSONArray [JSONString "TTInteractiveTask":JSONEncode{|*|} a0]] //DOES NOT INCLUDE a1
JSONEncode{|TaskTree|} (TTMonitorTask a0 a1)
= [JSONArray [JSONString "TTMonitorTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1]]
JSONEncode{|TaskTree|} (TTInstructionTask a0 a1 a2)
= [JSONArray [JSONString "TTInstructionTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1 ++ JSONEncode{|*|} a2]]
JSONEncode{|TaskTree|} (TTRpcTask a0 a1)
= [JSONArray [JSONString "TTRpcTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1]]
JSONEncode{|TaskTree|} (TTSequenceTask a0 a1)
= [JSONArray [JSONString "TTSequenceTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1]]
JSONEncode{|TaskTree|} (TTParallelTask a0 a1 a2)
= [JSONArray [JSONString "TTParallelTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1 ++ JSONEncode{|*|} a2]]
JSONEncode{|TaskTree|} (TTGroupedTask a0 a1 a2 a3)
= [JSONArray [JSONString "TTGroupedTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1 ++ JSONEncode{|*|} a3]] //DOES NOT INCLUDE a2
JSONEncode{|TaskTree|} (TTFinishedTask a0 a1)
= [JSONArray [JSONString "TTFinishedTask":JSONEncode{|*|} a0 ++ JSONEncode{|*|} a1]]
JSONEncode{|Timestamp|} (Timestamp x) = JSONEncode{|*|} x
JSONDecode{|Timestamp|} [JSONInt x:c] = (Just (Timestamp x),c)
import StdDebug
taskService :: !String !Bool ![String] !HTTPRequest *TSt -> (!HTTPResponse, !*TSt)
taskService url html path req tst
# (mbSessionErr,tst) = initSession sessionParam tst
......@@ -37,6 +59,13 @@ taskService url html path req tst
# items = taskItems processes
# json = JSONObject [("success",JSONBool True),("tasks",toJSON items)]
= (serviceResponse html "tasks" url listParams json, tst)
//For debugging, list all tasks in the process table
["debug"]
| isJust mbSessionErr
= (serviceResponse html "tasks debug" url debugParams (jsonSessionErr mbSessionErr), tst)
# (processes,tst) = getProcesses [Active,Suspended,Finished,Excepted,Deleted] tst
# json = JSONObject [("success",JSONBool True),("tasks",toJSON processes)]
= (serviceResponse html "tasks debug" url debugParams json, tst)
//Start a new task (create a process)
["create"]
| isJust mbSessionErr
......@@ -64,6 +93,18 @@ taskService url html path req tst
Just proc
# json = JSONObject [("success",JSONBool True),("task",toJSON (taskItem proc))]
= (serviceResponse html "task details" url detailsParams json, tst)
//Dump the raw tasktree datastructure
[taskId,"debug"]
| isJust mbSessionErr
= (serviceResponse html "task debug" url debugParams (jsonSessionErr mbSessionErr), tst)
# (mbProcess, tst) = getProcess taskId tst
= case mbProcess of
Nothing
= (notFoundResponse req, tst)
Just proc
# (tree,tst) = calculateTaskTree taskId [] tst
# json = JSONObject [("success",JSONBool True),("task",toJSON proc),("tree",toJSON tree)]
= (serviceResponse html "task debug" url debugParams json, tst)
//Show / Update task user interface definition
[taskId,"tui"]
| isJust mbSessionErr
......@@ -79,8 +120,8 @@ taskService url html path req tst
# task = taskItem proc
//Updates are posted as a list of triplets
# updates = case (fromJSON (fromString updatesParam)) of
Just updates = trace_n ("woot" +++ printToString updates ) updates
Nothing = trace_n "bummer" []
Just updates = updates
Nothing = []
//The menusChanged parameter is a global flag that is set when any task in the tree has
//changed the menu and thus the menu needs to be replaced
# (tree,tst=:{TSt|menusChanged})
......@@ -180,6 +221,8 @@ where
listParams = [("_session",sessionParam,True),("_user",userParam,False)]
debugParams = [("_session",sessionParam,True)]
detailsParams = [("_session",sessionParam,True)]
tuiParams = [("_session",sessionParam,True),("updates",updatesParam,False)]
updatesParam = paramValue "updates" req
......
......@@ -4,7 +4,7 @@ import StdEnv
import Http, TSt
import TaskTree
import JSON
import Util, Trace, Text
import Util, Text
import UserDB, ProcessDB, DocumentDB
import GenVisualize, GenUpdate, TUIDefinition
import TaskPanel
......@@ -20,14 +20,12 @@ handleWorkTabRequest req tst=:{staticInfo,menusChanged}
# subject = [properties.managerProperties.ManagerProperties.subject]
# user = staticInfo.currentSession.Session.user
# panel = buildTaskPanel task menus menusChanged user
// Collect debug information
# (debuginfo,tst) = if debug (collectDebugInfo tree tst) (Nothing, tst)
// Check the user who has to do the work: if not the correct user, give task redundant message.
| user == properties.managerProperties.ManagerProperties.worker || isMember user [u \\ (p,u) <- properties.systemProperties.subTaskWorkers]
// Update the task timestamps
# tst = updateTimeStamps properties.systemProperties.SystemProperties.taskId tst
// Create the response
= let content = {TaskContent| success = True, properties = Just properties, subject = subject, content = panel, debug = debuginfo} in
= let content = {TaskContent| success = True, properties = Just properties, subject = subject, content = panel} in
({http_emptyResponse & rsp_data = toString (toJSON content)}, tst)
| otherwise
......@@ -45,10 +43,10 @@ where
error msg tst
= ({http_emptyResponse & rsp_data = "{ \"success\" : false, \"error\" : \"" +++ msg +++ "\"}"}, tst)
redundant tst
= let content = {TaskContent| success = True, properties = Nothing, subject = [], content = TaskRedundant, debug = Nothing} in
= let content = {TaskContent| success = True, properties = Nothing, subject = [], content = TaskRedundant} in
({http_emptyResponse & rsp_data = toString (toJSON content)}, tst)
finished tst
= let content = {TaskContent| success = True, properties = Nothing, subject = [], content = TaskDone, debug = Nothing} in
= let content = {TaskContent| success = True, properties = Nothing, subject = [], content = TaskDone} in
({http_emptyResponse & rsp_data = toString (toJSON content)}, tst)
......@@ -62,14 +60,9 @@ where
, properties :: Maybe TaskProperties
, subject :: [String]
, content :: TaskPanel
, debug :: Maybe DebugInfo
}
:: DebugInfo =
{ tasktree :: String
}
derive JSONEncode TaskContent, DebugInfo
derive JSONEncode TaskContent
// === UTILITY FUNCTIONS ===
updateTimeStamps :: !ProcessId !*TSt -> *TSt
......@@ -78,12 +71,6 @@ updateTimeStamps pid tst
= snd (updateProcessProperties pid (\p -> {p & systemProperties = {p.systemProperties & firstEvent = case p.systemProperties.firstEvent of Nothing = Just now; x = x
, latestEvent = Just now
}}) tst)
collectDebugInfo :: TaskTree *TSt -> (Maybe DebugInfo, *TSt)
collectDebugInfo tree tst
# tasktree = traceTaskTree tree
= (Just {DebugInfo | tasktree = toString (toJSON tasktree)}, tst)
isFinished :: TaskTree -> Bool
isFinished (TTFinishedTask _ _) = True
isFinished _ = False
......
definition module Trace
/**
* This module provides functions for inspecting and visualizing
* the important datastructures of the iTasks framework
*/
from Html import :: HtmlTag
from ProcessDB import :: Process
from TaskTree import :: TaskTree
import JSON
:: TraceTree =
{ cls :: String
, user :: String
, uiProvider :: String
, leaf :: Bool
, iconCls :: String
, taskId :: String
, taskLabel :: String
, taskClass :: String
, children :: [TraceTree]
}
derive JSONEncode TraceTree
derive JSONDecode TraceTree
traceProcesses :: [Process] -> HtmlTag
traceTaskTree :: TaskTree -> TraceTree
traceTaskForest :: [TaskTree] -> String
\ No newline at end of file
implementation module Trace
import StdList, StdTuple
import Html, Text
import ProcessDB
import TaskTree
import JSON
derive JSONEncode TraceTree
derive JSONDecode TraceTree
derive bimap Maybe, (,)
traceProcesses :: [Process] -> HtmlTag
traceProcesses processes = mkTable processes
where
mkTable processes = TableTag [ClassAttr "debug-table"] [mkHeader: [mkRow process \\ process <- processes]]
mkHeader = TrTag [] [ThTag [] [Text "Id"],ThTag [] [Text "Subject"],ThTag [] [Text "Owner"],ThTag [] [Text "Subtask access"], ThTag [] [Text "Delegator"], ThTag [] [Text "Status"],ThTag [] [Text "Parent"],ThTag [] [Text "Mutable"],ThTag [] [Text "In Open Parallel?" ], ThTag [] [Text "Delete when done"] ]
mkRow process = TrTag [] [ TdTag [] [Text process.Process.taskId]
, TdTag [] [Text process.Process.properties.managerProperties.subject]
, TdTag [] [Text (toString process.Process.properties.managerProperties.ManagerProperties.worker)]
, TdTag [] [Text (foldr (+++) "" ["("+++toString p +++": "+++toString u+++") " \\ (p,u) <- process.Process.properties.systemProperties.subTaskWorkers])]
, TdTag [] [Text (toString process.Process.properties.systemProperties.manager)]
, TdTag [] [Text (toString process.Process.properties.systemProperties.SystemProperties.status)]
, TdTag [] (case process.Process.properties.systemProperties.parent of
Nothing = [Text "N/A"]
Just x = [Text x]
)
, TdTag [] [Text (printToString process.mutable)]
, TdTag [] [Text (printToString process.inParallelType)]
, TdTag [] [Text (printToString process.Process.properties.systemProperties.deleteWhenDone)]
]
traceTaskTree :: TaskTree -> TraceTree
traceTaskTree tree = mkTree tree
where
mkTree (TTInteractiveTask info _)
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = True
, iconCls = "task-int"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "INT"
, children = []
}
mkTree (TTMonitorTask info _ )
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = True
, iconCls = "task-mon"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "MON"
, children = []
}
mkTree (TTInstructionTask info _ _)
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = True
, iconCls = "task-ins"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "INS"
, children = []
}
mkTree (TTRpcTask info _ )
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = True
, iconCls = "task-rpc"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "RPC"
, children = []
}
mkTree (TTSequenceTask info trees)
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = checkIfLeaf trees
, iconCls = "task-seq"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "SEQ"
, children = [traceTaskTree tree \\ tree <- trees]
}
mkTree (TTParallelTask info tpi trees)
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = checkIfLeaf trees
, iconCls = "task-par"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "PAR"
, children = [traceTaskTree tree \\ tree <- trees]
}
mkTree (TTGroupedTask info trees _ _)
= { cls = "master-task"
, uiProvider = "col"
, user = ""
, leaf = checkIfLeaf trees
, iconCls = "task-grp"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "GRP"
, children = [traceTaskTree tree \\ tree <- trees]
}
mkTree (TTMainTask info mti menus inptype tree)
= { cls = "master-task"
, uiProvider = "col"
, user = toString mti.TaskProperties.managerProperties.ManagerProperties.worker
, leaf = False
, iconCls = "task-mnt"
, taskId = info.TaskInfo.taskId
, taskLabel = toString (Text info.TaskInfo.taskLabel)
, taskClass = "MNT"
, children = [traceTaskTree tree]
}
mkTree (TTFinishedTask info _)
= { cls = "master-task"
, user = ""
, uiProvider = "col"
, leaf = True
, iconCls = "task-fin"