implementation module htmlTask // (c) MJP 2006 - 2007 import StdEnv, StdHtml derive gForm [], Void, Maybe derive gUpd [], Void, Maybe derive gParse Void, Maybe derive gPrint Void, Maybe derive gerda Void :: *TSt = { tasknr :: !TaskNr // for generating unique form-id's , activated :: !Bool // if true activate task, if set as result task completed , userId :: !Int // id of user to which task is assigned , currentUserId :: !Int // id of application user , html :: !HtmlTree // accumulator for html code , storageInfo :: !Storage // iData lifespan and storage format , trace :: !Maybe [Trace] // for displaying task trace , hst :: !HSt // iData state } :: TaskNr :== [Int] // task nr i.j is adminstrated as [j,i] :: HtmlTree = BT [BodyTag] // simple code | (@@:) infix 0 (Int,String) HtmlTree// code with id of user attached to it | (-@:) infix 0 Int HtmlTree// skip code with this id if it is the id of the user | (+-+) infixl 1 HtmlTree HtmlTree // code to be placed next to each other | (+|+) infixl 1 HtmlTree HtmlTree // code to be placed below each other :: Storage = { tasklife :: !Lifespan , taskstorage :: !StorageFormat , taskmode :: !Mode } :: Trace = Trace TraceInfo [Trace] // traceinfo with possibly subprocess :: TraceInfo :== Maybe (Bool,(Int,TaskNr,String,String)) // Task finished? who did it, task nr, task name (for tracing) value produced // setting global iData options for tasks instance setTaskAttr Lifespan where setTaskAttr lifespan tst = {tst & storageInfo.tasklife = lifespan} instance setTaskAttr StorageFormat where setTaskAttr storageformat tst = {tst & storageInfo.taskstorage = storageformat} instance setTaskAttr Mode where setTaskAttr mode tst = {tst & storageInfo.taskmode = mode} // wrappers startTask :: !Int !Bool !(Task a) !*HSt -> (a,[BodyTag],!*HSt) | iCreate a startTask thisUser traceOn taska hst # (a,body,tst) = startTstTask thisUser traceOn taska tst = (a,body,tst.hst) where tst = { tasknr = [-1] , activated = True , currentUserId = thisUser , userId = defaultUser , html = BT [] , trace = Nothing , hst = hst , storageInfo = {tasklife = Session, taskstorage = PlainString, taskmode = Edit }} startNewTask :: !Int !Bool !(Task a) -> Task a | iCreateAndPrint a startNewTask newUser traceOn taska = mkTask "startNewTask" startNewTask` where startNewTask` tst=:{html} # (a,body,tst) = startTstTask newUser traceOn taska {tst & html = BT [], currentUserId = newUser, userId = defaultUser, tasknr = [-1]} = (a,{tst & html = html +|+ BT body}) singleUserTask :: !Int !Bool !(Task a) !*HSt -> (Html,*HSt) | iCreate a singleUserTask userId traceOn task hst # (_,html,hst) = startTask userId traceOn task hst = mkHtml "stest" html hst multiUserTask :: !Int !Bool !(Task a) !*HSt -> (Html,*HSt) | iCreate a multiUserTask nusers traceOn task hst # (idform,hst) = FuncMenu (Init,nFormId "User_Selected" (0,[("User " +++ toString i,\_ -> i) \\ i<-[0..nusers - 1] ])) hst # currentWorker = snd idform.value # (_,html,hst) = startTask currentWorker traceOn task hst = mkHtml "mtest" (ifTraceOn idform.form ++ html) hst where ifTraceOn form = if traceOn form [] multiUserTask2 :: !(!Int,!Int) !Int !Bool !(Task a) !*HSt -> (Html,*HSt) | iCreate a multiUserTask2 (minutes,seconds) nusers traceOn task hst # (idform,hst) = FuncMenu (Init,nFormId "User_Selected" (0,[("User " +++ toString i,\_ -> i) \\ i<-[0..nusers - 1] ])) hst # currentWorker = snd idform.value # (_,html,hst) = startTask currentWorker traceOn task hst = mkxHtml "mtest" (idform.form ++ html) hst where mkxHtml s tags hst = (Html (header s) (body tags),hst) header s = Head [`Hd_Std [Std_Title s]] [Hd_Script [] (autoRefresh minutes seconds)] body tags = Body [onloadBody] tags onloadBody = `Batt_Events [OnLoad (SScript scriptName)] scriptName = "beginrefresh()" startTstTask :: !Int !Bool !(Task a) !*TSt -> (a,[BodyTag],!*TSt) | iCreate a startTstTask thisUser traceOn taska tst=:{hst} | thisUser < 0 # (a,tst=:{html}) = taska {tst & hst = hst} = (a, noFilter html, {tst & html = html}) # userVersionNr = "User" <+++ thisUser <+++ "_VersionPNr" # sessionVersionNr = "User" <+++ thisUser <+++ "_VersionSNr" # traceId = "User" <+++ thisUser <+++ "_Trace" # (pversion,hst) = mkStoreForm (Init, pFormId userVersionNr 0) id hst # (refresh,hst) = simpleButton userVersionNr "Refresh" id hst # (traceAsked,hst) = simpleButton traceId "ShowTrace" (\_ -> True) hst # doTrace = traceAsked.value False # (sversion,hst) = mkStoreForm (Init, nFormId sessionVersionNr pversion.value) (if refresh.changed (\_ -> pversion.value) id) hst | sversion.value < pversion.value = (createDefault, refresh.form ++ [Br,Br, Hr [],Br] <|.|> [Font [Fnt_Color (`Colorname Yellow)] [B [] "Sorry, cannot apply command.",Br, B [] "Your page is not up-to date!",Br]],{tst & hst = hst}) # (a,tst=:{html,hst,trace}) = taska {tst & hst = hst, trace = if doTrace (Just []) Nothing} # (pversion,hst) = mkStoreForm (Init, pFormId userVersionNr 0) inc hst # (sversion,hst) = mkStoreForm (Init, nFormId sessionVersionNr pversion.value) inc hst # (selbuts,selname,seltask,hst) = Filter thisUser defaultUser ((defaultUser,"Main") @@: html) hst = (a, refresh.form ++ ifTraceOn traceAsked.form ++ [Br,Hr [],showUser thisUser,Br,Br] ++ if (doTrace && traceOn) [ printTrace2 trace ] [ STable [] [ [BodyTag selbuts, selname <||> seltask ] ] ] ,{tst & hst = hst}) where ifTraceOn form = if traceOn form [] mkSTable2 :: [[BodyTag]] -> BodyTag mkSTable2 table = Table [] (mktable table) where mktable table = [Tr [] (mkrow rows) \\ rows <- table] mkrow rows = [Td [Td_VAlign Alo_Top] [row] \\ row <- rows] Filter id user tree hst # (_,accu) = Collect ((==) id) user [] tree | isNil accu = ([],[],[],hst) # (names,tasks) = unzip accu # info = { tasklife = Session, taskstorage = PlainString, taskmode = Edit} # (selected,buttons,chosenname,hst) = mkTaskButtons "Main Tasks:" ("User" <+++ id) [] info names hst = (buttons,chosenname,tasks!!if (selected >= length accu) 0 selected,hst) Collect pred user accu ((nuser,taskname) @@: tree) # (myhtml,accu) = Collect pred nuser accu tree | pred nuser && not (isNil myhtml) = ([],[(taskname,myhtml):accu]) | otherwise = ([],accu) Collect pred user accu (BT bdtg) | pred user = (bdtg,accu) | otherwise = ([],accu) Collect pred user accu (tree1 +|+ tree2) # (lhtml,accu) = Collect pred user accu tree1 # (rhtml,accu) = Collect pred user accu tree2 = (lhtml <|.|> rhtml,accu) Collect pred user accu (tree1 +-+ tree2) # (lhtml,accu) = Collect pred user accu tree1 # (rhtml,accu) = Collect pred user [] tree2 = ([lhtml <=> rhtml],accu) Collect pred user accu (nuser -@: tree) = Collect (\v -> pred v && ((<>) nuser v)) user accu tree // = Collect (\v -> (<>) nuser v) user accu tree isNil [] = True isNil _ = False noFilter (BT body) = body noFilter (_ @@: html) = noFilter html noFilter (_ -@: html) = noFilter html noFilter (htmlL +-+ htmlR) = [noFilter htmlL <=> noFilter htmlR] noFilter (htmlL +|+ htmlR) = noFilter htmlL <|.|> noFilter htmlR mkTaskButtons :: !String !String !TaskNr !Storage ![String] *HSt -> (Int,[BodyTag],[BodyTag],*HSt) mkTaskButtons header myid tasknr info btnnames hst # btnsId = itaskId tasknr (myid <+++ "_Btns") # myidx = length btnnames # (chosen,hst) = SelectStore (myid,myidx) tasknr info id hst // which choice was made in the past # (buttons,hst) = SelectButtons Init btnsId info (chosen,btnnames) hst // create buttons # (chosen,hst) = SelectStore (myid,myidx) tasknr info buttons.value hst // maybe a new button was pressed # (buttons,hst) = SelectButtons Set btnsId info (chosen,btnnames) hst // adjust look of that button = (chosen,[red header, Br: buttons.form],[yellow (btnnames!!chosen),Br,Br],hst) where SelectButtons init id info (idx,btnnames) hst = TableFuncBut2 (init,cFormId info id [[(mode idx n, but txt,\_ -> n)] \\ txt <- btnnames & n <- [0..]] <@ Page) hst but i = LButton defpixel i mode i j | i==j = Display = Edit SelectStore :: !(String,Int) !TaskNr !Storage (Int -> Int) *HSt -> (Int,*HSt) SelectStore (myid,idx) tasknr info fun hst # storeId = itaskId tasknr (myid <+++ "_Select" <+++ idx) # (storeform,hst) = mkStoreForm (Init,cFormId info storeId 0) fun hst = (storeform.value,hst) // make an iTask editor editTask :: String a -> (Task a) | iData a editTask prompt a = mkTask "editTask" (editTask` prompt a) editTask` prompt a tst=:{tasknr,html,hst} # taskId = itaskId tasknr "_Seq" # editId = itaskId tasknr "_Val" # buttonId = itaskId tasknr "_But" # (taskdone,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId False) id hst // remember if the task has been done | taskdone.value // test if task has completed # (editor,hst) = (mkEditForm (Init,cdFormId tst.storageInfo editId a <@ Display) hst) // yes, read out current value, make editor passive = (editor.value,{tst & activated = True, html = html +|+ BT editor.form, hst = hst}) // return result task # (editor,hst) = mkEditForm (Init,cFormId tst.storageInfo editId a) hst // no, read out current value from active editor # (finbut,hst) = simpleButton buttonId prompt (\_ -> True) hst // add button for marking task as done # (taskdone,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId False) finbut.value hst // remember task status for next time | taskdone.value = editTask` prompt a {tst & hst = hst} // task is now completed, handle as previously = (editor.value,{tst & activated = taskdone.value, html = html +|+ BT (editor.form ++ finbut.form), hst = hst}) // monads for combining itasks (=>>) infix 1 :: (Task a) (a -> Task b) -> Task b (=>>) a b = a `bind` b (#>>) infixl 1 :: (Task a) (Task b) -> Task b (#>>) a b = a `bind` (\_ -> b) return_V :: a -> (Task a) | iCreateAndPrint a return_V a = mkTask "return_V" (return a) ireturn_V :: a -> (Task a) // for internal use, not shown in trace... ireturn_V a = return a return_D :: a -> (Task a) | gForm {|*|}, iCreateAndPrint a return_D a = mkTask "return_D" return_Display` where return_Display` tst = (a,{tst & html = tst.html +|+ BT [toHtml a ]}) // return result task return_VF :: a [BodyTag] -> (Task a) | iCreateAndPrint a return_VF a bodytag = mkTask "return_VF" return_VF` where return_VF` tst = (a,{tst & html = tst.html +|+ BT bodytag}) (<|) infix 6 :: (Task a) (a -> .Bool, a -> [BodyTag]) -> Task a | iCreate a (<|) taska (pred,message) = doTask where doTask tst=:{html = ohtml,activated} | not activated = (createDefault,tst) # (a,tst=:{activated,html= nhtml}) = taska {tst & html = BT []} | not activated || pred a = (a,{tst & html = ohtml +|+ nhtml}) = doTask {tst & html = ohtml +|+ BT (message a)} (<<@) infix 3 :: (Task a) b -> (Task a) | setTaskAttr b (<<@) task attr = doTask where doTask tst=:{storageInfo} # tst = setTaskAttr attr tst # (a,tst) = task (setTaskAttr attr tst) = (a,{tst & storageInfo = storageInfo}) (?>>) infix 5 :: [BodyTag] (Task a) -> (Task a) | iCreate a (?>>) prompt task = doTask where doTask tst=:{html=ohtml,activated} | not activated = (createDefault,tst) # (a,tst=:{activated,html=nhtml}) = task {tst & html = BT []} | activated = (a,{tst & html = ohtml}) = (a,{tst & html = ohtml +|+ BT prompt +|+ nhtml}) (!>>) infix 5 :: [BodyTag] (Task a) -> (Task a) | iCreate a (!>>) prompt task = doTask where doTask tst=:{html=ohtml,activated=myturn} | not myturn = (createDefault,tst) # (a,tst=:{html=nhtml}) = task {tst & html = BT []} = (a,{tst & html = ohtml +|+ BT prompt +|+ nhtml}) // Task makers are wrappers which take care of // - deciding whether a task should be called (activated) or not // - adding trace information // - generating task numbers in a systematic way // It is very important that the numbering of the tasks is done systematically // Every task should have a unique number // Every sequential task should increase the task number // If a task j is a subtask of task i, than it will get number i.j in reverse order mkTask :: !String (Task a) -> (Task a) | iCreateAndPrint a mkTask taskname mytask = mkTaskNoInc taskname mytask o incTaskNr mkTaskNoInc :: !String (Task a) -> (Task a) | iCreateAndPrint a // common second part of task wrappers mkTaskNoInc taskname mytask = mkTaskNoInc` where mkTaskNoInc` tst=:{activated,tasknr,userId} | not activated = (createDefault,tst) // not active, don't call task, return default value # (val,tst=:{activated,trace}) = mytask tst // active, so perform task and get its result | isNothing trace || taskname == "" = (val,tst) // no trace, just return value = (val,{tst & tasknr = tasknr , trace = Just (InsertTrace activated tasknr userId taskname (printToString val) (fromJust trace))}) // adjust trace incTaskNr tst = {tst & tasknr = incNr tst.tasknr} newSubTaskNr tst = {tst & tasknr = [-1:tst.tasknr]} incNr [] = [0] incNr [i:is] = [i+1:is] addTasknr [] j = [j] addTasknr [i:is] j = [i+j:is] ///////////////////////////////////// repeatTask_Std :: (a -> Task a) (a -> Bool) -> a -> Task a | iCreateAndPrint a repeatTask_Std task pred = \a -> mkTask "repeatTask_Std" (dorepeatTask_Std a) where dorepeatTask_Std a tst # (na,tst) = task a (newSubTaskNr tst) | pred na = (na,tst) = dorepeatTask_Std na (incTaskNr tst) ///////////////////////////////////// // non optimized versions of foreverTask and newTask will increase the task tree stack and // therefore cannot be used for big applications foreverTask_Std :: (Task a) -> Task a | iCreateAndPrint a foreverTask_Std task = mkTask "foreverTask_Std" doforeverTask_Std where doforeverTask_Std tst # (_,tst) = task (newSubTaskNr tst) = foreverTask_Std task tst newTask_Std :: !String (Task a) -> (Task a) | iCreateAndPrint a newTask_Std taskname mytask = mkTask taskname (mytask o newSubTaskNr) // same, but by remembering task results stack space can be saved foreverTask :: (Task a) -> Task a | iData a foreverTask task = foreverTask` where foreverTask` tst=:{tasknr,hst} # mytasknr = incNr tasknr // manual incr task nr # taskId = itaskId mytasknr "_Rep" // create store id # (currtasknr,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId mytasknr) id hst // fetch actual tasknr # (val,tst=:{activated,hst})= mkTaskNoInc "foreverTask" foreverTask`` {tst & tasknr = currtasknr.value,hst = hst} | activated // task is completed # ntasknr = incNr currtasknr.value // incr tasknr # (currtasknr,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId tasknr) (\_ -> ntasknr) hst // store next task nr = mkTaskNoInc "foreverTask" foreverTask`` {tst & tasknr = currtasknr.value, hst = hst} // initialize new task = (val,tst) where foreverTask`` tst=:{tasknr} # (val,tst)= task {tst & tasknr = [-1:tasknr]} // do task to repeat = (val,{tst & tasknr = tasknr}) newTask :: !String (Task a) -> (Task a) | iData a newTask taskname mytask = mkTask taskname (newTask` False mytask) newTask` collect mytask tst=:{tasknr,hst} # taskId = itaskId tasknr "_New" # (taskval,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId (False,createDefault)) id hst // remember if the task has been done # (taskdone,taskvalue) = taskval.value | taskdone = (taskvalue,{tst & hst = hst}) // optimize: return stored value # (val,tst=:{activated,hst})= mytask {tst & tasknr = [-1:tasknr],hst =hst} // do task, first shift tasknr | not activated = (val,{tst & tasknr = tasknr}) // subtask not ready, return value of subtasks # tst=:{hst} = if collect (deleteSubTasks [0:tasknr] {tst & tasknr = [0:tasknr]}) tst # (_,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId (False,createDefault)) (\_ -> (True,val)) hst // remember if the task has been done = (val,{tst & tasknr = tasknr, hst = hst}) // same, but additionally deleting subtasks foreverTask_GC :: (Task a) -> Task a | iCreateAndPrint a foreverTask_GC task = mkTask "foreverTask_GC" foreverTask` where foreverTask` tst=:{tasknr} # (val,tst=:{activated}) = task {tst & tasknr = [-1:tasknr]} // shift tasknr | activated = foreverTask` (deleteSubTasks tasknr {tst & tasknr = tasknr}) // loop = (val,tst) newTask_GC :: !String (Task a) -> (Task a) | iData a newTask_GC taskname mytask = mkTask taskname (newTask` True mytask) deleteSubTasks :: !TaskNr TSt -> TSt deleteSubTasks tasknr tst=:{hst} = {tst & hst = deleteIData (subtasksids tasknr) hst} where subtasksids tasknr formid # prefix = itaskId tasknr "" # lprefix = size prefix # lformid = size formid = prefix <= formid && lformid > lprefix // parallel subtask creation utility mkParSubTask :: !String !Int (Task a) -> (Task a) | iCreateAndPrint a // two shifts are needed mkParSubTask name i task = mkParSubTask` where mkParSubTask` tst=:{tasknr} # (v,tst) = mkTaskNoInc (name <+++ "." <+++ i) mysubtask {tst & tasknr = [i:tasknr],activated = True} // shift task = (v,{tst & tasknr = tasknr}) where mysubtask tst=:{tasknr} = task {tst & tasknr = [-1:tasknr], activated = True} // shift once again! // assigning tasks to users, each user is identified by a number (@:) infix 3 :: !(!String,!Int) (Task a) -> (Task a) | iCreate a (@:) (taskname,nuserId) taska = \tst=:{userId} -> assignTask` userId {tst & userId = nuserId} where assignTask` userId tst=:{html=ohtml,activated} | not activated = (createDefault,tst) # (a,tst=:{html=nhtml,activated}) = taska {tst & html = BT [],userId = nuserId} // activate task of indicated user | activated = (a,{tst & activated = True , userId = userId // work is done , html = ohtml +|+ // clear screen ((nuserId,taskname) @@: nhtml)}) = (a,{tst & userId = userId // restore user Id , html = ohtml +|+ BT [Br, Txt ("Waiting for Task "), yellow taskname, Txt " from ", showUser nuserId,Br] +|+ ((nuserId,taskname) @@: BT [Txt "Requested by ", showUser userId,Br,Br] +|+ nhtml)}) // combine html code, filter later (@::) infix 3 :: !Int (Task a) -> (Task a) | iCreate a (@::) nuserId taska = \tst=:{userId} -> assignTask` userId {tst & userId = nuserId} where assignTask` userId tst=:{html,activated} | not activated = (createDefault,tst) # (a,tst=:{html=nhtml,activated}) = taska {tst & html = BT [],userId = nuserId} // activate task of indicated user | activated = (a,{tst & userId = userId // work is done , html = html}) = (a,{tst & userId = userId // restore user Id , html = html +|+ BT [Br, Txt "Waiting for ", yellow ("Task " <+++ userId), Txt " from ", showUser nuserId,Br] +|+ ((nuserId,"Task " <+++ userId) @@: BT [Txt "Requested by ", showUser userId,Br,Br] +|+ nhtml)}) // combine html code, filter later // sequential tasks internEditSTask tracename prompt task = \tst -> mkTask tracename (editTask` prompt task) tst seqTasks :: [(String,Task a)] -> (Task [a])| iCreateAndPrint a seqTasks options = mkTask "seqTasks" seqTasks` where seqTasks` tst=:{tasknr} # (val,tst) = doseqTasks options [] {tst & tasknr = [-1:tasknr]} = (val,{tst & tasknr = tasknr}) doseqTasks [] accu tst = (reverse accu,{tst & activated = True}) doseqTasks [(taskname,task):ts] accu tst=:{html} # (a,tst=:{activated=adone,html=ahtml}) = task {tst & activated = True, html = BT []} | not adone = (reverse accu,{tst & html = html +|+ BT [yellow taskname,Br,Br] +|+ ahtml}) = doseqTasks ts [a:accu] {tst & html = html +|+ ahtml} // choose one or more tasks out of a collection buttonTask :: String (Task a) -> (Task a) | iCreateAndPrint a buttonTask s task = iCTask_button "buttonTask" [(s,task)] iCTask_button tracename options = mkTask tracename (dochooseTask options) chooseTask :: [(String,Task a)] -> (Task a) | iCreateAndPrint a chooseTask options = mkTask "chooseTask" (dochooseTask options) dochooseTask [] tst = ireturn_V createDefault tst dochooseTask options tst=:{tasknr,html,hst} // choose one subtask out of the list # taskId = itaskId tasknr ("_Or0." <+++ length options) # buttonId = itaskId tasknr "_But" # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) id hst | chosen.value == -1 # (choice,hst) = TableFuncBut (Init,cFormId tst.storageInfo buttonId [[(but txt,\_ -> n) \\ txt <- map fst options & n <- [0..]]] <@ Page) hst # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) choice.value hst | chosen.value == -1 = (createDefault,{tst & activated =False,html = html +|+ BT choice.form, hst = hst}) # chosenTask = snd (options!!chosen.value) # (a,tst=:{activated=adone,html=ahtml,hst}) = chosenTask {tst & tasknr = [-1:tasknr], activated = True, html = BT [], hst = hst} = (a,{tst & tasknr = tasknr, activated = adone, html = html +|+ ahtml,hst = hst}) # chosenTask = snd (options!!chosen.value) # (a,tst=:{activated=adone,html=ahtml,hst}) = chosenTask {tst & tasknr = [-1:tasknr], activated = True, html = BT [], hst = hst} = (a,{tst & tasknr = tasknr, activated = adone, html = html +|+ ahtml,hst = hst}) but i = LButton defpixel i chooseTask_pdm :: [(String,Task a)] -> (Task a) |iCreateAndPrint a chooseTask_pdm options = mkTask "chooseTask_pdm" (dochooseTask_pdm options) where dochooseTask_pdm [] tst = (createDefault,{tst& activated = True}) dochooseTask_pdm options tst=:{tasknr,html,hst} // choose one subtask out of the list # taskId = itaskId tasknr ("_Or0." <+++ length options) # (choice,hst) = FuncMenu (Init,cFormId tst.storageInfo taskId (0,[(txt,id) \\ txt <- map fst options])) hst # (_,tst=:{activated=adone,html=ahtml}) = internEditSTask "" "Done" Void {tst & activated = True, html = BT [], hst = hst,tasknr = [-1:tasknr]} | not adone = (createDefault,{tst & activated = False, html = html +|+ BT choice.form +|+ ahtml, tasknr = tasknr}) # chosenIdx = snd choice.value # chosenTask = snd (options!!chosenIdx) # (a,tst=:{activated=bdone,html=bhtml,hst}) = chosenTask {tst & activated = True, html = BT [], tasknr = [0:tasknr]} = (a,{tst & activated = adone&&bdone, html = html +|+ bhtml,hst = hst, tasknr = tasknr}) mchoiceTasks :: [(String,Task a)] -> (Task [a]) | iCreateAndPrint a mchoiceTasks options = mkTask "mchoiceTask" (domchoiceTasks options) where domchoiceTasks [] tst = ([],{tst& activated = True}) domchoiceTasks options tst=:{tasknr,html,hst} // choose one subtask out of the list # taskId = itaskId tasknr ("_MLC." <+++ length options) # (cboxes,hst) = ListFuncCheckBox (Init,cFormId tst.storageInfo taskId initCheckboxes) hst # optionsform = cboxes.form <=|> [Txt text \\ (text,_) <- options] # (_,tst=:{html=ahtml,activated = adone}) = (internEditSTask "" "OK" Void <<@ Page) {tst & activated = True, html = BT [],hst = hst,tasknr = [-1:tasknr]} | not adone = seqTasks [] {tst & html=html +|+ BT [optionsform] +|+ ahtml,tasknr = [0:tasknr]} # mytasks = [option \\ option <- options & True <- snd cboxes.value] # (val,tst) = seqTasks mytasks {tst & tasknr = [0:tasknr]} = (val,{tst & tasknr = tasknr}) initCheckboxes = [(CBNotChecked text, \ b bs id -> id) \\ (text,_) <- options] // tasks ending as soon as one of its subtasks completes (-||-) infixr 3 :: (Task a) (Task a) -> (Task a) | iCreateAndPrint a (-||-) taska taskb = mkTask "-||-" (doOrTask (taska,taskb)) orTask :: (Task a,Task a) -> (Task a) | iCreateAndPrint a orTask (taska,taskb) = mkTask "orTask" (doOrTask (taska,taskb)) doOrTask (taska,taskb) tst=:{tasknr,html,hst} # taskId = itaskId tasknr "orTaskChosen" # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) id hst | chosen.value == 0 // a chosen # (a,tst=:{html=ahtml}) = mkParSubTask "orTask" 0 taska {tst & tasknr = tasknr, html = BT [], hst = hst} = (a,{tst & html = html +|+ ahtml}) | chosen.value == 1 // b chosen # (b,tst=:{html=bhtml}) = mkParSubTask "orTask" 1 taskb {tst & tasknr = tasknr, html = BT [], hst = hst} = (b,{tst & html = html +|+ bhtml}) # (a,tst=:{activated=adone,html=ahtml}) = mkParSubTask "orTask" 0 taska {tst & tasknr = tasknr, html = BT [], hst = hst} | adone # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) (\_ -> 0) hst = (a,{tst & html = html +|+ ahtml}) # (b,tst=:{activated=bdone,html=bhtml}) = mkParSubTask "orTask" 1 taskb {tst & tasknr = tasknr, html = BT []} | bdone # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) (\_ -> 1) hst = (b,{tst & html = html +|+ bhtml}) = (a,{tst & activated = False, html = html +|+ ahtml +|+ bhtml}) orTask2 :: (Task a,Task b) -> (Task (EITHER a b)) | iCreateAndPrint a & iCreateAndPrint b orTask2 (taska,taskb) = mkTask "orTask2" (doorTask2 (taska,taskb)) where doorTask2 (taska,taskb) tst=:{tasknr,html,hst} # taskId = itaskId tasknr "orTask2Chosen" # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) id hst | chosen.value == 0 // a chosen # (a,tst=:{html=ahtml}) = mkParSubTask "orTask" 0 taska {tst & tasknr = tasknr, html = BT [], hst = hst} = (LEFT a,{tst & html = html +|+ ahtml}) | chosen.value == 1 // b chosen # (b,tst=:{html=bhtml}) = mkParSubTask "orTask" 1 taskb {tst & tasknr = tasknr, html = BT [], hst = hst} = (RIGHT b,{tst & html = html +|+ bhtml}) # (a,tst=:{activated=adone,html=ahtml}) = mkParSubTask "orTask" 0 taska {tst & tasknr = tasknr, html = BT [], hst = hst} | adone # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) (\_ -> 0) hst = (LEFT a,{tst & html = html +|+ ahtml}) # (b,tst=:{activated=bdone,html=bhtml}) = mkParSubTask "orTask" 1 taskb {tst & tasknr = tasknr, html = BT []} | bdone # (chosen,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) (\_ -> 1) hst = (RIGHT b,{tst & html = html +|+ bhtml}) = (LEFT a,{tst & activated = False, html = html +|+ ahtml +|+ bhtml}) checkAnyTasks traceid taskoptions (ctasknr,skipnr) (bool,which) tst=:{tasknr} | ctasknr == length taskoptions = (bool,which,tst) | ctasknr == skipnr = checkAnyTasks traceid taskoptions (inc ctasknr,skipnr) (bool,which) tst # task = taskoptions!!ctasknr # (a,tst=:{activated = adone}) = mkParSubTask traceid ctasknr task {tst & tasknr = tasknr, activated = True} = checkAnyTasks traceid taskoptions (inc ctasknr,skipnr) (bool||adone,if adone ctasknr which) {tst & tasknr = tasknr, activated = True} orTasks :: [(String,Task a)] -> (Task a) | iCreateAndPrint a orTasks options = mkTask "orTasks" (doorTasks options) where doorTasks [] tst = ireturn_V createDefault tst doorTasks tasks tst=:{tasknr,html,hst,userId} # taskId = itaskId tasknr "orTasksChosen" # (chosenS,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) id hst | chosenS.value <> -1 // task has been finished already # chosenTask = snd (options!!chosenS.value) # (a,tst) = chosenTask {tst & tasknr = [-1,chosenS.value:tasknr], activated = True, html = BT [], hst = hst} = (a,{tst & activated = True, html = html}) # (chosen,buttons,chosenname,hst) = mkTaskButtons "or Tasks:" "or" tasknr tst.storageInfo (map fst options) hst # (finished,which,tst=:{html=allhtml})= checkAnyTasks "orTasks" (map snd options) (0,chosen) (False,0) {tst & html = BT [], hst = hst, activated = True} # chosenvalue = if finished which chosen // it can be the case that someone else has finshed one of the tasks # chosenTaskName = fst (options!!chosenvalue) # chosenTask = snd (options!!chosenvalue) # (a,tst=:{activated=adone,html=ahtml}) = chosenTask {tst & tasknr = [-1,chosenvalue:tasknr], activated = True, html = BT []} | not adone = (a,{tst & activated = adone , html = html +|+ BT buttons +-+ (BT chosenname +|+ ahtml) +|+ (userId -@: allhtml) }) # (chosenS,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId -1) (\_ -> chosenvalue) hst // remember finished task for next tim = (a,{tst & activated = adone, html = html}) // Parallel tasks ending if all complete (-&&-) infixr 4 :: (Task a) (Task b) -> (Task (a,b)) | iCreateAndPrint a & iCreateAndPrint b (-&&-) taska taskb = mkTask "-&&-" (doAndTask (taska,taskb)) andTask :: (Task a,Task b) -> (Task (a,b)) | iCreateAndPrint a & iCreateAndPrint b andTask (taska,taskb) = mkTask "andTask" (doAndTask (taska,taskb)) doAndTask (taska,taskb) tst=:{tasknr,html} # (a,tst=:{activated=adone,html=ahtml}) = mkParSubTask "andTask" 0 taska {tst & html = BT []} # (b,tst=:{activated=bdone,html=bhtml}) = mkParSubTask "andTask" 1 taskb {tst & tasknr = tasknr, html = BT []} = ((a,b),{tst & activated = adone&&bdone, html = html +|+ ahtml +|+ bhtml}) andTasks :: [(String,Task a)] -> (Task [a]) | iCreateAndPrint a andTasks options = mkTask "andTasks" (doandTasks options) where doandTasks [] tst = ireturn_V [] tst doandTasks options tst=:{tasknr,html,userId,hst} # (alist,tst=:{activated=finished,hst=hst}) = checkAllTasks "andTasks" options (0,-1) True [] {tst & html = BT [], activated = True,hst=hst} | finished = (map snd alist,{tst & html = html}) # tst=:{hst} = tst # (chosen,buttons,chosenname,hst) = mkTaskButtons "and Tasks:" "and" tasknr tst.storageInfo (map fst options) hst # chosenTask = snd (options!!chosen) # chosenTaskName = fst (options!!chosen) # (a,{activated=adone,html=ahtml,hst=hst}) = mkParSubTask "andTasks" chosen chosenTask {tst & tasknr = tasknr, activated = True, html = BT [], hst = hst} # (alist,tst=:{activated=finished,html=allhtml,hst=hst}) = checkAllTasks "andTasks" options (0,chosen) True [] {tst & html = BT [], activated = True,hst=hst} | not adone = ([a],{tst & hst = hst , activated = False , html = html +|+ BT buttons +-+ (BT chosenname +|+ ahtml) +|+ (userId -@: allhtml) }) # (alist,{activated=finished,html=allhtml,hst = hst}) = checkAllTasks "PTasks" options (0,chosen) True [] {tst & html = BT [],hst =hst} | finished = (map snd alist,{tst & hst = hst, activated = finished, html = html}) = (map snd alist,{tst & hst = hst , activated = finished , html = html +|+ BT buttons +-+ (BT chosenname +|+ ahtml) +|+ (userId -@: allhtml) }) checkAllTasks traceid options (ctasknr,skipnr) bool alist tst=:{tasknr} | ctasknr == length options = (reverse alist,{tst & activated = bool}) | ctasknr == skipnr = checkAllTasks traceid options (inc ctasknr,skipnr) bool alist tst # (taskname,task) = options!!ctasknr # (a,tst=:{activated = adone}) = mkParSubTask traceid ctasknr task {tst & tasknr = tasknr, activated = True} = checkAllTasks traceid options (inc ctasknr,skipnr) (bool&&adone) [(taskname,a):alist] {tst & tasknr = tasknr, activated = True} andTasks_mstone :: [(String,Task a)] -> (Task [(String,a)]) | iCreateAndPrint a andTasks_mstone options = mkTask "andTasks_mstone" (PMilestoneTasks` options) where PMilestoneTasks` [] tst = ireturn_V [] tst PMilestoneTasks` options tst=:{tasknr,html,userId} # (alist,tst=:{activated=finished,html=allhtml}) = checkAllTasks "andTasks" options (0,-1) True [] {tst & html = BT [], activated = True} | finished = (alist,{tst & html = html}) # tst=:{hst} = tst # (chosen,buttons,chosenname,hst) = mkTaskButtons "and Tasks:" "and" tasknr tst.storageInfo (map fst options) hst # chosenTask = snd (options!!chosen) # chosenTaskName = fst (options!!chosen) # (a,{activated=adone,html=ahtml,hst=hst}) = mkParSubTask "andTasks" chosen chosenTask {tst & tasknr = tasknr, activated = True, html = BT [], hst = hst} # (milestoneReached,_,{hst}) = checkAnyTasks "andTasks_mstone" (map snd options) (0,-1) (False,-1) {tst & html = BT [], hst = hst} | not adone = (alist,{tst & hst = hst , activated = adone || milestoneReached , html = html +|+ BT buttons +-+ (BT chosenname +|+ ahtml) +|+ (userId -@: allhtml) }) # (alist,{activated=finished,html=allhtml,hst = hst}) = checkAllTasks "PTasks" options (0,chosen) True [] {tst & html = BT [],hst =hst} | finished = (alist,{tst & hst = hst, activated = finished, html = html }) = (alist,{tst & hst = hst , activated = finished || milestoneReached , html = html +|+ BT buttons +-+ (BT chosenname +|+ ahtml) +|+ (userId -@: allhtml) }) andTasks_mu :: String [(Int,Task a)] -> (Task [a]) | iData a andTasks_mu taskid tasks = newTask "andTasks_mu" (domu_andTasks tasks) where domu_andTasks list = andTasks [(taskid <+++ " " <+++ i, i @:: task) \\ (i,task) <- list] // very experimental higher order lazy task stuf (-!>) infix 4 :: (Task s) (Task a) -> (Task (Maybe s,TClosure a)) | iCreateAndPrint s & iCreateAndPrint a (-!>) stoptask task = mkTask "-!>" stop` where stop` tst=:{tasknr,html} # (val,tst=:{activated = taskdone,html = taskhtml}) = task {tst & activated = True, html = BT [], tasknr = normalTaskId} # (s, tst=:{activated = stopped, html = stophtml}) = stoptask {tst & activated = True, html = BT [], tasknr = stopTaskId} | stopped = return_V (Just s,TClosure (close task)) {tst & html = html, activated = True} | taskdone = return_V (Nothing,TClosure (return_V val)) {tst & html = html +|+ taskhtml, activated = True} = return_V (Nothing,TClosure (return_V val)) {tst & html = html +|+ taskhtml +|+ stophtml, activated = False} where close t = \tst -> t {tst & tasknr = normalTaskId} stopTaskId = [-1,0:tasknr] normalTaskId = [-1,1:tasknr] channel :: String (Task a) -> (Task (TClosure a,TClosure a)) | iCreateAndPrint a channel name task = mkTask "channel" doSplit where doSplit tst=:{tasknr} = return_V (TClosure (close task),TClosure (hclose task)) tst where close task = \tst -> task {tst & tasknr = tasknr} hclose task = \tst -> nohtml task {tst & tasknr = tasknr} nohtml task tst # (val,tst=:{activated}) = task tst | activated = (val,{tst & html = BT []}) = (val,{tst & html = BT [Txt ("Waiting for completion of "<+++ name)]}) // time and date related tasks waitForTimeTask:: HtmlTime -> (Task HtmlTime) waitForTimeTask time = mkTask "waitForTimeTask" waitForTimeTask` where waitForTimeTask` tst=:{tasknr,hst} # taskId = itaskId tasknr "_Time_" # (stime,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId time) id hst // remember time # ((currtime,_),hst) = getTimeAndDate hst | currtime < stime.value= (stime.value,{tst & activated = False,hst = hst}) = (currtime - stime.value,{tst & hst = hst}) waitForTimerTask:: HtmlTime -> (Task HtmlTime) waitForTimerTask time = waitForTimerTask` where waitForTimerTask` tst=:{hst} # ((ctime,_),hst) = getTimeAndDate hst = waitForTimeTask (ctime + time) {tst & hst = hst} waitForDateTask:: HtmlDate -> (Task HtmlDate) waitForDateTask date = mkTask "waitForDateTask" waitForDateTask` where waitForDateTask` tst=:{tasknr,hst} # taskId = itaskId tasknr "_Date_" # (taskdone,hst) = mkStoreForm (Init,cFormId tst.storageInfo taskId (False,date)) id hst // remember date # ((_,currdate),hst) = getTimeAndDate hst | currdate < date = (date,{tst & activated = False, hst = hst}) = (date,{tst & hst = hst}) // functions on TSt taskId :: TSt -> (Int,TSt) taskId tst=:{userId} = (userId,tst) userId :: TSt -> (Int,TSt) userId tst=:{currentUserId} = (currentUserId,tst) addHtml :: [BodyTag] TSt -> TSt addHtml bodytag tst=:{activated, html} | not activated = tst // not active, return default value = {tst & html = html +|+ BT bodytag} // active, so perform task or get its result // lifters to iTask state (*>>) infix 4 :: (TSt -> (a,TSt)) (a -> Task b) -> (Task b) (*>>) ftst b = doit where doit tst # (a,tst) = ftst tst = b a tst (@>>) infix 4 :: (TSt -> TSt) (Task a) -> Task a (@>>) ftst b = doit where doit tst # tst = ftst tst = b tst appIData :: (IDataFun a) -> (Task a) | iData a appIData idatafun = \tst -> mkTask "appIData" (appIData` idatafun) tst where appIData` idata tst=:{tasknr,html,hst} # (idata,hst) = idatafun hst # (_,{tasknr,activated,html=ahtml,hst}) = internEditSTask "appIDataDone" "Done" Void {tst & activated = True, html = BT [],hst = hst} = (idata.value,{tst & tasknr = tasknr,activated = activated, html = html +|+ (if activated (BT idata.form) (BT idata.form +|+ ahtml)), hst = hst}) appHSt2 :: (HSt -> (a,HSt)) -> (Task a) | iData a appHSt2 fun = mkTask "appHSt" doit where doit tst=:{hst} # (value,hst) = fun hst = (value,{tst & hst = hst, activated = True}) // task is now completed, handle as previously appHSt :: (HSt -> (a,HSt)) -> (Task a) | iData a appHSt fun = mkTask "appHSt" doit where doit tst=:{activated,html,tasknr,hst,storageInfo} # taskId = itaskId tasknr "appHst" # (store,hst) = mkStoreForm (Init,cFormId storageInfo taskId (False,createDefault)) id hst # (done,value) = store.value | done = (value,{tst & hst = hst}) // if task has completed, don't do it again # (value,hst) = fun hst # (store,hst) = mkStoreForm (Init,cFormId storageInfo taskId (False,createDefault)) (\_ -> (True,value)) hst // remember task status for next time # (done,value) = store.value = (value,{tst & activated = done, hst = hst}) // task is now completed, handle as previously Once :: (Task a) -> (Task a) | iData a Once fun = mkTask "Once" doit where doit tst=:{activated,html,tasknr,hst,storageInfo} # taskId = itaskId tasknr "_Once_" # (store,hst) = mkStoreForm (Init,cFormId storageInfo taskId (False,createDefault)) id hst # (done,value) = store.value | done = (value,{tst & hst = hst}) // if task has completed, don't do it again # (value,tst=:{hst})= fun {tst & hst = hst} # (store,hst) = mkStoreForm (Init,cFormId storageInfo taskId (False,createDefault)) (\_ -> (True,value)) hst // remember task status for next time # (done,value) = store.value = (value,{tst & activated = done, hst = hst}) // task is now completed, handle as previously // Notice that when combining tasks the context restrictions on certain types will get stronger // It can vary from : no restriction on a -> iTrace a -> iData a // In most cases the user can simply ask Clean to derive the corresponding generic functions // For the type Task this will not work since it is a higher order type // Therefore when yielding a task as result of a task, // the type Task need to be wrapped into TClosure for which the generic functions are defined below // Tested for iTrace, will not work for iData gPrint{|TClosure|} gpa a ps = ps <<- "Task Closure" gUpd{|TClosure|} gc (UpdSearch _ 0) c = (UpdDone, c) gUpd{|TClosure|} gc (UpdSearch val cnt) c = (UpdSearch val (cnt - 2),c) gUpd{|TClosure|} gc (UpdCreate l) _ # (mode,default) = gc (UpdCreate l) undef = (UpdCreate l, TClosure (\tst -> (default,tst))) gUpd{|TClosure|} gc mode b = (mode, b) gForm{|TClosure|} gfa (init,formid) hst = ({value=formid.ival,changed=False,form=[]},hst) /* convertTask task = dynamic_to_string (dynamic task::*TSt -> *(a^,*TSt)) string_to_dynamic` s = string_to_dynamic ( {s` \\ s` <-: s}) */ // *** utility section *** // editors cFormId {tasklife,taskstorage,taskmode} s d = {sFormId s d & lifespan = tasklife, storage = taskstorage, mode = taskmode} cdFormId {tasklife,taskstorage,taskmode} s d = {sdFormId s d & lifespan = tasklife, storage = taskstorage, mode = taskmode} // simple html code generation utilities showUser nr = yellow ("User " <+++ nr) yellow message = Font [Fnt_Color (`Colorname Yellow)] [B [] message] silver message = Font [Fnt_Color (`Colorname Silver)] [B [] message] red message = Font [Fnt_Color (`Colorname Red)] [B [] message] // task number generation showTaskNr [] = "" showTaskNr [i] = toString i showTaskNr [i:is] = showTaskNr is <+++ "." <+++ toString i itaskId :: !TaskNr String -> String itaskId nr postfix = "iTask_" <+++ (showTaskNr nr) <+++ postfix InsertTrace :: !Bool !TaskNr !Int String !String ![Trace] -> [Trace] InsertTrace finished idx who taskname val trace = InsertTrace` ridx who val trace where InsertTrace` :: !TaskNr !Int !String ![Trace] -> [Trace] InsertTrace` [i] who str traces | i < 0 = abort ("negative task numbers:" <+++ showTaskNr idx <+++ "," <+++ who <+++ "," <+++ taskname) # (Trace _ itraces) = select i traces = updateAt` i (Trace (Just (finished,(who,show,taskname,str))) itraces) traces InsertTrace` [i:is] who str traces | i < 0 = abort ("negative task numbers:" <+++ showTaskNr idx <+++ "," <+++ who <+++ "," <+++ taskname) # (Trace ni itraces) = select i traces # nistraces = InsertTrace` is who str itraces = updateAt` i (Trace ni nistraces) traces select :: !Int ![Trace] -> Trace select i list | i < length list = list!!i = Trace Nothing [] show = idx //showTaskNr idx ridx = reverse idx updateAt`:: !Int !Trace ![Trace] -> [Trace] updateAt` n x list | n < 0 = abort "negative numbers not allowed" = updateAt` n x list where updateAt`:: !Int !Trace ![Trace] -> [Trace] updateAt` 0 x [] = [x] updateAt` 0 x [y:ys] = [x:ys] updateAt` n x [] = [Trace Nothing [] : updateAt` (n-1) x []] updateAt` n x [y:ys] = [y : updateAt` (n-1) x ys] printTrace2 Nothing = EmptyBody printTrace2 (Just a) = STable emptyBackground (print False a) where print _ [] = [] print b trace = [pr b x ++ [STable emptyBackground (print (isDone x||b) xs)]\\ (Trace x xs) <- trace] pr _ Nothing = [] pr dprev (Just (dtask,(w,i,tn,s))) | dprev && (not dtask) = pr False Nothing // subtask not important anymore (assume no milestone tasks) | not dtask = showTask2 cellattr1b White Navy Maroon Silver (w,i,tn,s) = showTask2 cellattr1a White Yellow Red White (w,i,tn,s) showTask2 attr1 c1 c2 c3 c4 (w,i,tn,s) = [Table doneBackground [ Tr [] [Td attr1 [font c1 (toString (last (reverse i)))], Td cellattr2 [font c2 tn]] , Tr [] [Td attr1 [font c3 (toString w)], Td cellattr2 [font c4 s]] ] ,Br] showTask att c1 c2 c3 c4 (w,i,tn,s) = [STable doneBackground [ [font c1 (toString w),font c2 ("T" <+++ showTaskNr i)] , [EmptyBody, font c3 tn] , [EmptyBody, font c4 s] ] ] isDone Nothing = False isDone (Just (b,(w,i,tn,s))) = b doneBackground = [ Tbl_CellPadding (Pixels 1), Tbl_CellSpacing (Pixels 0), cellwidth , Tbl_Rules Rul_None, Tbl_Frame Frm_Border ] doneBackground2 = [ Tbl_CellPadding (Pixels 0), Tbl_CellSpacing (Pixels 0), cellwidth ] emptyBackground = [Tbl_CellPadding (Pixels 0), Tbl_CellSpacing (Pixels 0)] cellattr1a = [Td_Bgcolor (`Colorname Green), Td_Width (Pixels 10), Td_VAlign Alo_Absmiddle] cellattr1b = [Td_Bgcolor (`Colorname Silver), Td_Width (Pixels 10), Td_VAlign Alo_Absmiddle] cellattr2 = [Td_VAlign Alo_Top] cellwidth = Tbl_Width (Pixels 130) font color message = Font [Fnt_Color (`Colorname color), Fnt_Size -1] [B [] message]