Commit 0f9e3220 authored by Alexandre Delanoë's avatar Alexandre Delanoë

Merge remote-tracking branch 'origin/async-workers' into dev

parents 27d6aa0f c2d0cae5
...@@ -79,6 +79,6 @@ ...@@ -79,6 +79,6 @@
"xhr2": "~0.2.1" "xhr2": "~0.2.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"purescript-language-server": "~0.17.1" "purescript-language-server": "~0.18.2"
} }
} }
module Gargantext.AsyncTasks ( module Gargantext.AsyncTasks (
TaskList Task
, TaskList
, Storage(..) , Storage(..)
, readAsyncTasks
, insert , insert
, finish , finish
, focus , focus
, asyncTaskTTriggersAppReload -- , asyncTaskTTriggersAppReload
, asyncTaskTTriggersTreeReload -- , asyncTaskTTriggersTreeReload
, asyncTaskTTriggersMainPageReload ) -- , asyncTaskTTriggersMainPageReload
)
where where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -33,7 +34,8 @@ import Toestand as T ...@@ -33,7 +34,8 @@ import Toestand as T
import Web.Storage.Storage as WSS import Web.Storage.Storage as WSS
type TaskList = Array GT.AsyncTaskWithType type Task = GT.WorkerTask
type TaskList = Array Task
newtype Storage = Storage (Map.Map GT.NodeID TaskList) newtype Storage = Storage (Map.Map GT.NodeID TaskList)
derive newtype instance Semigroup Storage derive newtype instance Semigroup Storage
derive newtype instance Monoid Storage derive newtype instance Monoid Storage
...@@ -47,32 +49,9 @@ instance JSON.WriteForeign Storage where ...@@ -47,32 +49,9 @@ instance JSON.WriteForeign Storage where
arr :: Array (Tuple String TaskList) arr :: Array (Tuple String TaskList)
arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s) arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s)
readAsyncTasks :: Effect Storage
readAsyncTasks = R2.loadLocalStorageState' R2.asyncTasksKey mempty
-- readAsyncTasks = R2.getls >>= WSS.getItem R2.asyncTasksKey >>= handleMaybe
-- where
-- handleMaybe (Just val) = handleEither (parse val)
-- handleMaybe Nothing = pure empty
-- -- either parsing or decoding could fail, hence two errors
-- handleEither (Left err) = err *> pure empty
-- handleEither (Right ss) = pure ss
-- parse s = GU.mapLeft (log2 "Error parsing serialised sessions:") (JSON.readJSON s)
writeAsyncTasks :: Storage -> Effect Unit
writeAsyncTasks = R2.setLocalStorageState R2.asyncTasksKey
-- writeAsyncTasks storage = R2.getls >>= WSS.setItem R2.asyncTasksKey storage
modifyAsyncTasks :: (Storage -> Storage) -> Effect Unit
modifyAsyncTasks f = readAsyncTasks >>= writeAsyncTasks <<< f
modifyTaskBox :: (Storage -> Storage) -> T.Box Storage -> Effect Unit modifyTaskBox :: (Storage -> Storage) -> T.Box Storage -> Effect Unit
modifyTaskBox f box = do modifyTaskBox f box = T.modify_ f box
s <- T.read box -- modifyAsyncTasks (const newS)
let newS = f s
T.write_ newS box
modifyAsyncTasks (const newS)
getTasks :: GT.NodeID -> Storage -> TaskList getTasks :: GT.NodeID -> Storage -> TaskList
getTasks nodeId (Storage storage) = fromMaybe [] $ Map.lookup nodeId storage getTasks nodeId (Storage storage) = fromMaybe [] $ Map.lookup nodeId storage
...@@ -83,9 +62,9 @@ setTasks id tasks (Storage s) = Storage $ Map.insert id tasks s ...@@ -83,9 +62,9 @@ setTasks id tasks (Storage s) = Storage $ Map.insert id tasks s
focus :: GT.NodeID -> T.Box Storage -> R.Hooks (T.Box TaskList) focus :: GT.NodeID -> T.Box Storage -> R.Hooks (T.Box TaskList)
focus id tasks = T.useFocused (getTasks id) (setTasks id) tasks focus id tasks = T.useFocused (getTasks id) (setTasks id) tasks
removeTaskFromList :: TaskList -> GT.AsyncTaskWithType -> TaskList removeTaskFromList :: TaskList -> Task -> TaskList
removeTaskFromList ts (GT.AsyncTaskWithType { task: GT.AsyncTask { id: id' } }) = removeTaskFromList ts (GT.WorkerTask { message_id }) =
A.filter (\(GT.AsyncTaskWithType { task: GT.AsyncTask { id: id'' } }) -> id' /= id'') ts A.filter (\(GT.WorkerTask { message_id: message_id' }) -> message_id /= message_id') ts
type ReductorProps = ( type ReductorProps = (
reloadForest :: T2.ReloadS reloadForest :: T2.ReloadS
...@@ -93,39 +72,57 @@ type ReductorProps = ( ...@@ -93,39 +72,57 @@ type ReductorProps = (
, storage :: Storage , storage :: Storage
) )
insert :: GT.NodeID -> GT.AsyncTaskWithType -> T.Box Storage -> Effect Unit insert :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
insert id task storageBox = modifyTaskBox newStorage storageBox insert id task storageBox = modifyTaskBox newStorage storageBox
where where
newStorage (Storage s) = Storage $ Map.alter (maybe (Just [task]) (\ts -> Just $ A.cons task ts)) id s newStorage (Storage s) = Storage $ Map.alter (maybe (Just [task]) (\ts -> Just $ A.nub $ A.cons task ts)) id s
finish :: GT.NodeID -> GT.AsyncTaskWithType -> T.Box Storage -> Effect Unit finish :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
finish id task storage = remove id task storage finish id task storage = remove id task storage
remove :: GT.NodeID -> GT.AsyncTaskWithType -> T.Box Storage -> Effect Unit remove :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
remove id task storageBox = modifyTaskBox newStorage storageBox remove id task storageBox = modifyTaskBox newStorage storageBox
where where
newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s
-- AsyncTaskWithType is deprecated, but we leave these functions here,
-- becuase they're a useful reference
-- When a task is finished: which tasks cause forest or app reload -- When a task is finished: which tasks cause forest or app reload
asyncTaskTriggersAppReload :: GT.AsyncTaskType -> Boolean -- asyncTaskTriggersAppReload :: GT.AsyncTaskType -> Boolean
asyncTaskTriggersAppReload _ = false -- asyncTaskTriggersAppReload _ = false
-- asyncTaskTTriggersAppReload :: GT.AsyncTaskWithType -> Boolean
-- asyncTaskTTriggersAppReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersAppReload typ
-- asyncTaskTriggersMainPageReload :: GT.AsyncTaskType -> Boolean
-- asyncTaskTriggersMainPageReload GT.UpdateNgramsCharts = true
-- asyncTaskTriggersMainPageReload _ = false
-- asyncTaskTTriggersMainPageReload :: GT.AsyncTaskWithType -> Boolean
-- asyncTaskTTriggersMainPageReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersMainPageReload typ
-- asyncTaskTriggersTreeReload :: GT.AsyncTaskType -> Boolean
-- asyncTaskTriggersTreeReload GT.CorpusFormUpload = true
-- asyncTaskTriggersTreeReload GT.Query = true
-- asyncTaskTriggersTreeReload GT.UploadFile = true
-- asyncTaskTriggersTreeReload _ = false
-- asyncTaskTTriggersTreeReload :: GT.AsyncTaskWithType -> Boolean
-- asyncTaskTTriggersTreeReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersTreeReload typ
asyncTaskTTriggersAppReload :: GT.AsyncTaskWithType -> Boolean
asyncTaskTTriggersAppReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersAppReload typ
asyncTaskTriggersMainPageReload :: GT.AsyncTaskType -> Boolean -- With push-based notifications, it doesn't make sense to store jobs in localStorage
asyncTaskTriggersMainPageReload GT.UpdateNgramsCharts = true -- readAsyncTasks :: Effect Storage
asyncTaskTriggersMainPageReload _ = false -- readAsyncTasks = R2.loadLocalStorageState' R2.asyncTasksKey mempty
asyncTaskTTriggersMainPageReload :: GT.AsyncTaskWithType -> Boolean -- writeAsyncTasks :: Storage -> Effect Unit
asyncTaskTTriggersMainPageReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersMainPageReload typ -- writeAsyncTasks = R2.setLocalStorageState R2.asyncTasksKey
asyncTaskTriggersTreeReload :: GT.AsyncTaskType -> Boolean -- modifyAsyncTasks :: (Storage -> Storage) -> Effect Unit
asyncTaskTriggersTreeReload GT.CorpusFormUpload = true -- modifyAsyncTasks f = readAsyncTasks >>= writeAsyncTasks <<< f
asyncTaskTriggersTreeReload GT.Query = true
asyncTaskTriggersTreeReload GT.UploadFile = true
asyncTaskTriggersTreeReload _ = false
asyncTaskTTriggersTreeReload :: GT.AsyncTaskWithType -> Boolean -- removeTaskFromList :: TaskList -> GT.AsyncTaskWithType -> TaskList
asyncTaskTTriggersTreeReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersTreeReload typ -- removeTaskFromList ts (GT.AsyncTaskWithType { task: GT.AsyncTask { id: id' } }) =
-- A.filter (\(GT.AsyncTaskWithType { task: GT.AsyncTask { id: id'' } }) -> id' /= id'') ts
...@@ -93,9 +93,14 @@ mainAppCpt = here.component "main" cpt where ...@@ -93,9 +93,14 @@ mainAppCpt = here.component "main" cpt where
R.useEffectOnce' $ do R.useEffectOnce' $ do
void $ Sessions.load boxes.sessions void $ Sessions.load boxes.sessions
-- tasks <- GAT.useTasks boxes.reloadRoot boxes.reloadForest -- tasks <- GAT.useTasks boxes.reloadRoot boxes.reloadForest
R.useEffectOnce' $ do
tasksStorage <- GAT.readAsyncTasks -- NOTE Task storage is not needed with new-style notifications
T.write_ tasksStorage boxes.tasks -- and async workers. The tasks (with their pgoress) should be
-- pushed as soon as the worker computes the task's chunk
-- R.useEffectOnce' $ do
-- tasksStorage <- GAT.readAsyncTasks
-- T.write_ tasksStorage boxes.tasks
-- R.useEffectOnce' $ do -- R.useEffectOnce' $ do
-- T.write (Just tasksReductor) tasks -- T.write (Just tasksReductor) tasks
R.useEffectOnce' $ do R.useEffectOnce' $ do
......
...@@ -23,6 +23,7 @@ import Effect (Effect) ...@@ -23,6 +23,7 @@ import Effect (Effect)
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Timer (setTimeout) import Effect.Timer (setTimeout)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Store as Store import Gargantext.Components.App.Store as Store
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), ModalSizing(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), ModalSizing(..), Variant(..))
...@@ -169,15 +170,17 @@ docViewCpt = R2.hereComponent here "docView" hCpt where ...@@ -169,15 +170,17 @@ docViewCpt = R2.hereComponent here "docView" hCpt where
liftEffect $ liftEffect $
T.write_ true onDocumentCreationPendingBox T.write_ true onDocumentCreationPendingBox
eTask <- DFC.create session nodeId fdata _ <- DFC.create session nodeId fdata
handleRESTError hp errors eTask liftEffect $ here.log "[docView] TODO onCreateDocumentEnd handler"
\t -> liftEffect $ launchDocumentCreationProgress
errors -- handleRESTError hp errors eTask
session -- \t -> liftEffect $ launchDocumentCreationProgress
nodeId -- errors
t -- session
onCreateDocumentEnd -- nodeId
-- t
-- onCreateDocumentEnd
-- Render -- Render
pure $ pure $
...@@ -237,44 +240,44 @@ docViewCpt = R2.hereComponent here "docView" hCpt where ...@@ -237,44 +240,44 @@ docViewCpt = R2.hereComponent here "docView" hCpt where
] ]
] ]
launchDocumentCreationProgress :: -- launchDocumentCreationProgress ::
T.Box (Array GT.FrontendError) -- T.Box (Array GT.FrontendError)
-> Session -- -> Session
-> GT.ID -- -> GT.ID
-> GT.AsyncTaskWithType -- -> GAT.Task
-> (GT.AsyncProgress -> Effect Unit) -- -> (GT.AsyncProgress -> Effect Unit)
-> Effect Unit -- -> Effect Unit
launchDocumentCreationProgress errors session nodeId currentTask cbk -- launchDocumentCreationProgress errors session nodeId currentTask cbk
= void $ setTimeout 1000 $ launchAff_ $ -- = void $ setTimeout 1000 $ launchAff_ $
scanDocumentCreationProgress errors session nodeId currentTask cbk -- scanDocumentCreationProgress errors session nodeId currentTask cbk
scanDocumentCreationProgress :: -- scanDocumentCreationProgress ::
T.Box (Array GT.FrontendError) -- T.Box (Array GT.FrontendError)
-> Session -- -> Session
-> GT.ID -- -> GT.ID
-> GT.AsyncTaskWithType -- -> GAT.Task
-> (GT.AsyncProgress -> Effect Unit) -- -> (GT.AsyncProgress -> Effect Unit)
-> Aff Unit -- -> Aff Unit
scanDocumentCreationProgress errors session nodeId currentTask cbk = do -- scanDocumentCreationProgress errors session nodeId currentTask cbk = do
eTask <- DFC.createProgress session nodeId currentTask -- -- eTask <- DFC.createProgress session nodeId currentTask
handleRESTError (R2.herePrefix here "[scanDocumentCreationProgress]") errors eTask -- handleRESTError (R2.herePrefix here "[scanDocumentCreationProgress]") errors eTask
\asyncProgress -> liftEffect do -- \asyncProgress -> liftEffect do
let -- let
GT.AsyncProgress { status } = asyncProgress -- GT.AsyncProgress { status } = asyncProgress
endingStatusList = -- endingStatusList =
[ GT.IsFinished -- [ GT.IsFinished
, GT.IsKilled -- , GT.IsKilled
, GT.IsFailure -- , GT.IsFailure
] -- ]
hasEndingStatus s = any (eq s) endingStatusList -- hasEndingStatus s = any (eq s) endingStatusList
if (hasEndingStatus status) -- if (hasEndingStatus status)
then -- then
cbk asyncProgress -- cbk asyncProgress
else -- else
launchDocumentCreationProgress errors session nodeId currentTask cbk -- launchDocumentCreationProgress errors session nodeId currentTask cbk
--------------------------------------------------- ---------------------------------------------------
......
module Gargantext.Components.DocsTable.DocumentFormCreation module Gargantext.Components.DocsTable.DocumentFormCreation
( documentFormCreation ( documentFormCreation
, FormData , FormData
, create, createProgress , create
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -11,6 +11,7 @@ import Data.Either (Either(..)) ...@@ -11,6 +11,7 @@ import Data.Either (Either(..))
import Data.Foldable (foldl, intercalate) import Data.Foldable (foldl, intercalate)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Effect (Effect) import Effect (Effect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
import Gargantext.Config.REST (AffRESTError) import Gargantext.Config.REST (AffRESTError)
...@@ -288,41 +289,15 @@ create :: ...@@ -288,41 +289,15 @@ create ::
Session Session
-> GT.ID -> GT.ID
-> Record FormData -> Record FormData
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
create session nodeId = create session nodeId =
rename rename
>>> post session request >>> post session request
>=> case _ of
Left err -> pure $ Left err
Right task -> pure $ Right $ GT.AsyncTaskWithType
{ task
, typ: GT.NodeDocument
}
where where
request = GR.NodeAPI GT.Node (Just nodeId) request = GR.NodeAPI GT.Node (Just nodeId) (GT.asyncTaskTypePath GT.NodeDocument)
(GT.asyncTaskTypePath GT.NodeDocument)
rename = Record.rename rename = Record.rename
(Proxy :: Proxy "source") (Proxy :: Proxy "source")
(Proxy :: Proxy "sources") (Proxy :: Proxy "sources")
createProgress ::
Session
-> GT.ID
-> GT.AsyncTaskWithType
-> AffRESTError GT.AsyncProgress
createProgress
session
nodeId
(GT.AsyncTaskWithType { task: GT.AsyncTask { id } })
=
get session request
where
request = GR.NodeAPI GT.Node (Just nodeId)
(GT.asyncTaskTypePath GT.NodeDocument <> pollParams)
pollParams = "/" <> id <> "/poll?limit1"
...@@ -372,7 +372,8 @@ performAction = performAction' where ...@@ -372,7 +372,8 @@ performAction = performAction' where
updateNode params { boxes: { errors, tasks }, nodeId: id, session } = do updateNode params { boxes: { errors, tasks }, nodeId: id, session } = do
eTask <- updateRequest params session id eTask <- updateRequest params session id
handleRESTError (R2.herePrefix here "[updateNode]") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[updateNode]") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[performAction] TODO: IMPLEMENT ME!"
sharePublic params p@{ boxes: { errors }, session } = traverse_ f params where sharePublic params p@{ boxes: { errors }, session } = traverse_ f params where
f (SubTreeOut { in: inId, out }) = do f (SubTreeOut { in: inId, out }) = do
...@@ -386,12 +387,14 @@ performAction = performAction' where ...@@ -386,12 +387,14 @@ performAction = performAction' where
uploadFile' nodeType fileType fileFormat lang mName contents { boxes: { errors, tasks }, nodeId: id, session } selection = do uploadFile' nodeType fileType fileFormat lang mName contents { boxes: { errors, tasks }, nodeId: id, session } selection = do
eTask <- uploadFile { contents, fileType, fileFormat, lang, id, nodeType, mName, selection, session } eTask <- uploadFile { contents, fileType, fileFormat, lang, id, nodeType, mName, selection, session }
handleRESTError (R2.herePrefix here "[uploadFile']") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[uploadFile']") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[performAction] TODO: IMPLEMENT ME!"
uploadArbitraryFile' fileFormat mName blob { boxes: { errors, tasks }, nodeId: id, session } = do uploadArbitraryFile' fileFormat mName blob { boxes: { errors, tasks }, nodeId: id, session } = do
eTask <- uploadArbitraryFile session id { blob, fileFormat, mName } eTask <- uploadArbitraryFile session id { blob, fileFormat, mName }
handleRESTError (R2.herePrefix here "[uploadArbitraryFile']") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[uploadArbitraryFile']") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[performAction] TODO: IMPLEMENT ME!"
moveNode params p@{ boxes: { errors }, session } = traverse_ f params where moveNode params p@{ boxes: { errors }, session } = traverse_ f params where
f (SubTreeOut { in: in', out }) = do f (SubTreeOut { in: in', out }) = do
......
...@@ -30,7 +30,7 @@ import Gargantext.Components.Forest.Tree.Node.Action.WriteNodesDocuments (docume ...@@ -30,7 +30,7 @@ import Gargantext.Components.Forest.Tree.Node.Action.WriteNodesDocuments (docume
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..), fTreeID) import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..), fTreeID)
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..)) import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..))
import Gargantext.Components.Notifications as Notifications import Gargantext.Components.Notifications as Notifications
import Gargantext.Components.Notifications.Types as NotificationsT import Gargantext.Components.Notifications.Types as NT
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError) import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
...@@ -181,12 +181,17 @@ treeCpt = here.component "tree" cpt where ...@@ -181,12 +181,17 @@ treeCpt = here.component "tree" cpt where
-- rendered via 'childLoader'. However, we still need a hook -- rendered via 'childLoader'. However, we still need a hook
-- here, so that if e.g. the tree is pinned, it becomes its own -- here, so that if e.g. the tree is pinned, it becomes its own
-- root and we want to see notifications of it as well. -- root and we want to see notifications of it as well.
let cb _ = do let cb n = do
here.log2 "[tree] callback!" root case n of
-- The modal window has some problems closing when we refresh too early. This is a HACK NT.NUpdateTree _ -> do
void $ setTimeout 400 $ T2.reload reload here.log2 "[tree] update tree" root
let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree root) ("tree-" <> show root) cb -- The modal window has some problems closing when we refresh too early. This is a HACK
void $ setTimeout 400 $ T2.reload reload
NT.NUpdateWorkerProgress ji jl -> do
here.log3 "[tree] update worker progress" ji jl
_ -> pure unit
ws <- T.read boxes.wsNotification ws <- T.read boxes.wsNotification
let action = NT.InsertCallback (NT.UpdateTree root) ("tree-" <> show root) cb
Notifications.performAction ws action Notifications.performAction ws action
R.useEffect' do R.useEffect' do
...@@ -337,12 +342,15 @@ childLoaderCpt = R2.hereComponent here "childLoader" hCpt where ...@@ -337,12 +342,15 @@ childLoaderCpt = R2.hereComponent here "childLoader" hCpt where
boxes <- Store.use boxes <- Store.use
R.useEffectOnce' $ do R.useEffectOnce' $ do
let cb _ = do let cb n = do
here.log2 "[childLoader] callback!" p.id case n of
-- The modal window has some problems closing when we refresh too early. This is a HACK NT.NUpdateTree _nId -> do
void $ setTimeout 400 $ T2.reload reload here.log2 "[childLoader] update tree" p.id
let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree p.id) ("tree-" <> show p.id) cb -- The modal window has some problems closing when we refresh too early. This is a HACK
void $ setTimeout 400 $ T2.reload reload
_ -> pure unit
ws <- T.read boxes.wsNotification ws <- T.read boxes.wsNotification
let action = NT.InsertCallback (NT.UpdateTree p.id) ("tree-" <> show p.id) cb
Notifications.performAction ws action Notifications.performAction ws action
-- Render -- Render
...@@ -384,7 +392,8 @@ doSearch task { boxes: { tasks }, tree: NTree (LNode {id}) _ } = liftEffect $ do ...@@ -384,7 +392,8 @@ doSearch task { boxes: { tasks }, tree: NTree (LNode {id}) _ } = liftEffect $ do
updateNode params p@{ boxes: { errors, tasks }, session, tree: (NTree (LNode {id}) _) } = do updateNode params p@{ boxes: { errors, tasks }, session, tree: (NTree (LNode {id}) _) } = do
eTask <- updateRequest params session id eTask <- updateRequest params session id
handleRESTError (R2.herePrefix here "[updateNode]") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[updateNode]") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[updateNode] TODO: IMPLEMENT ME!"
closeBox p closeBox p
renameNode name p@{ boxes: { errors }, session, tree: (NTree (LNode {id}) _) } = do renameNode name p@{ boxes: { errors }, session, tree: (NTree (LNode {id}) _) } = do
...@@ -412,19 +421,22 @@ addNode' name nodeType p@{ boxes: { errors, forestOpen }, session, tree: (NTree ...@@ -412,19 +421,22 @@ addNode' name nodeType p@{ boxes: { errors, forestOpen }, session, tree: (NTree
uploadFile' nodeType fileType fileFormat lang mName contents p@{ boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } selection = do uploadFile' nodeType fileType fileFormat lang mName contents p@{ boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } selection = do
eTask <- uploadFile { contents, fileFormat, fileType, id, lang, mName, nodeType, selection, session } eTask <- uploadFile { contents, fileFormat, fileType, id, lang, mName, nodeType, selection, session }
handleRESTError (R2.herePrefix here "[uploadFile']") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[uploadFile']") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[uplaodFile'] TODO: IMPLEMENT ME!"
here.log2 "[uploadFile'] UploadFile, uploaded, task:" task here.log2 "[uploadFile'] UploadFile, uploaded, task:" task
closeBox p closeBox p
uploadArbitraryFile' fileFormat mName blob { boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } = do uploadArbitraryFile' fileFormat mName blob { boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } = do
eTask <- uploadArbitraryFile session id { blob, fileFormat, mName } eTask <- uploadArbitraryFile session id { blob, fileFormat, mName }
handleRESTError (R2.herePrefix here "[uploadArbitraryFile']") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[uploadArbitraryFile']") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[uplaodArbitraryFile'] TODO: IMPLEMENT ME!"
uploadFrameCalc' lang { boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } selection = do uploadFrameCalc' lang { boxes: { errors, tasks }, session, tree: (NTree (LNode { id }) _) } selection = do
eTask <- uploadFrameCalc session id lang selection eTask <- uploadFrameCalc session id lang selection
handleRESTError (R2.herePrefix here "[uploadFrameCalc']") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[uploadFrameCalc']") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[uplaodFrameCalc'] TODO: IMPLEMENT ME!"
moveNode params p@{ boxes: { errors, forestOpen }, session } = traverse_ f params where moveNode params p@{ boxes: { errors, forestOpen }, session } = traverse_ f params where
f (SubTreeOut { in: in', out }) = do f (SubTreeOut { in: in', out }) = do
...@@ -448,7 +460,8 @@ linkNode nodeType params p@{ boxes: { errors }, session } = traverse_ f params w ...@@ -448,7 +460,8 @@ linkNode nodeType params p@{ boxes: { errors }, session } = traverse_ f params w
documentsFromWriteNodes params p@{ boxes: { errors, tasks }, session, tree: NTree (LNode { id }) _ } = do documentsFromWriteNodes params p@{ boxes: { errors, tasks }, session, tree: NTree (LNode { id }) _ } = do
eTask <- documentsFromWriteNodesReq session params eTask <- documentsFromWriteNodesReq session params
handleRESTError (R2.herePrefix here "[documentsFromWriteNodes]") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[documentsFromWriteNodes]") errors eTask $ \task -> liftEffect $ do
GAT.insert id task tasks -- GAT.insert id task tasks
here.log "[documentsFromWriteNodes] TODO: IMPLEMENT ME!"
pure unit pure unit
refreshTree p refreshTree p
......
...@@ -14,6 +14,7 @@ import Data.String.Regex as Regex ...@@ -14,6 +14,7 @@ import Data.String.Regex as Regex
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff) import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Timer (setTimeout)
import Gargantext.AsyncTasks as GAT import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Store as AppStore import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
...@@ -28,6 +29,8 @@ import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, node ...@@ -28,6 +29,8 @@ import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, node
import Gargantext.Components.GraphExplorer.API as GraphAPI import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Components.Lang (Lang(EN)) import Gargantext.Components.Lang (Lang(EN))
import Gargantext.Components.Nodes.Corpus.Types (CorpusData) import Gargantext.Components.Nodes.Corpus.Types (CorpusData)
import Gargantext.Components.Notifications as Notifications
import Gargantext.Components.Notifications.Types as NT
import Gargantext.Context.Progress (asyncContext, asyncProgress) import Gargantext.Context.Progress (asyncContext, asyncProgress)
import Gargantext.Ends (Frontends, url) import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
...@@ -174,27 +177,29 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -174,27 +177,29 @@ nodeSpanCpt = here.component "nodeSpan" cpt
onTaskFinish :: onTaskFinish ::
GT.NodeID GT.NodeID
-> GT.AsyncTaskWithType -> GAT.Task
-> Unit -> Unit
-> Effect Unit -> Effect Unit
onTaskFinish id' t _ = do onTaskFinish id' t _ = do
GAT.finish id' t boxes.tasks GAT.finish id' t boxes.tasks
if GAT.asyncTaskTTriggersAppReload t then do -- TODO App reload!
here.log2 "reloading root for task" t here.log "[onTaskFinish] TODO APP/TREE/MAIN PAGE RELOAD"
T2.reload boxes.reloadRoot -- if GAT.asyncTaskTTriggersAppReload t then do
else do -- here.log2 "reloading root for task" t
if GAT.asyncTaskTTriggersTreeReload t then do -- T2.reload boxes.reloadRoot
here.log2 "reloading tree for task" t -- else do
T2.reload reload -- if GAT.asyncTaskTTriggersTreeReload t then do
else do -- here.log2 "reloading tree for task" t
here.log2 "task doesn't trigger a tree reload" t -- T2.reload reload
pure unit -- else do
if GAT.asyncTaskTTriggersMainPageReload t then do -- here.log2 "task doesn't trigger a tree reload" t
here.log2 "reloading main page for task" t -- pure unit
T2.reload boxes.reloadMainPage -- if GAT.asyncTaskTTriggersMainPageReload t then do
else do -- here.log2 "reloading main page for task" t
here.log2 "task doesn't trigger a main page reload" t -- T2.reload boxes.reloadMainPage
pure unit -- else do
-- here.log2 "task doesn't trigger a main page reload" t
-- pure unit
-- snd tasks $ GAT.Finish id' t -- snd tasks $ GAT.Finish id' t
-- mT <- T.read tasks -- mT <- T.read tasks
-- case mT of -- case mT of
...@@ -230,6 +235,26 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -230,6 +235,26 @@ nodeSpanCpt = here.component "nodeSpan" cpt
else else
H.div {} [] H.div {} []
-- Notifications
R.useEffectOnce' $ do
let cb n = do
case n of
NT.NUpdateTree _ -> do
here.log2 "[nodeSpan] update tree" props.id
-- The modal window has some problems closing when we refresh too early. This is a HACK
void $ setTimeout 400 $ T2.reload reload
NT.NUpdateWorkerProgress ji atl -> do
-- TODO Fire this only once!
-- here.log3 "[nodeSpan] update job progress" ji atl
if GT.asyncTaskLogIsFinished atl
then pure unit
else
GAT.insert props.id ji boxes.tasks
_ -> pure unit
ws <- T.read boxes.wsNotification
let action = NT.InsertCallback (NT.UpdateTree props.id) ("node-span-" <> show props.id) cb
Notifications.performAction ws action
-- Render -- Render
pure $ pure $
......
...@@ -8,6 +8,7 @@ import Data.Newtype (class Newtype) ...@@ -8,6 +8,7 @@ import Data.Newtype (class Newtype)
import Data.String (Pattern(..), indexOf) import Data.String (Pattern(..), indexOf)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..)) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox) import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools import Gargantext.Components.Forest.Tree.Node.Tools as Tools
...@@ -39,10 +40,9 @@ addNode session parentId = post session $ GR.NodeAPI GT.Node (Just parentId) "" ...@@ -39,10 +40,9 @@ addNode session parentId = post session $ GR.NodeAPI GT.Node (Just parentId) ""
addNodeAsync :: Session addNodeAsync :: Session
-> GT.ID -> GT.ID
-> AddNodeValue -> AddNodeValue
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
addNodeAsync session parentId q = do addNodeAsync session parentId q = do
eTask :: Either RESTError GT.AsyncTask <- post session p q post session p q
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.AddNode }) <$> eTask
where where
p = GR.NodeAPI GT.Node (Just parentId) (GT.asyncTaskTypePath GT.AddNode) p = GR.NodeAPI GT.Node (Just parentId) (GT.asyncTaskTypePath GT.AddNode)
......
...@@ -4,6 +4,7 @@ import Gargantext.Prelude ...@@ -4,6 +4,7 @@ import Gargantext.Prelude
import Data.Either (Either) import Data.Either (Either)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..)) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (LinkNodeReq(..), UpdateNodeParams(..)) import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (LinkNodeReq(..), UpdateNodeParams(..))
import Gargantext.Components.Forest.Tree.Node.Tools as Tools import Gargantext.Components.Forest.Tree.Node.Tools as Tools
...@@ -20,11 +21,11 @@ import Toestand as T ...@@ -20,11 +21,11 @@ import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Link" here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Link"
linkNodeReq :: Session -> Maybe GT.NodeType -> GT.ID -> GT.ID -> AffRESTError GT.AsyncTaskWithType linkNodeReq :: Session -> Maybe GT.NodeType -> GT.ID -> GT.ID -> AffRESTError GAT.Task
linkNodeReq session nt fromId toId = do linkNodeReq session nt fromId toId = do
eTask :: Either RESTError GT.AsyncTask <- post session (NodeAPI GT.Node (Just fromId) "update") post session (NodeAPI GT.Node (Just fromId) "update")
(LinkNodeReq { nodeType: linkNodeType nt, id: toId }) (LinkNodeReq { nodeType: linkNodeType nt, id: toId })
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.UpdateNode }) <$> eTask
linkNodeType :: Maybe GT.NodeType -> GT.NodeType linkNodeType :: Maybe GT.NodeType -> GT.NodeType
linkNodeType (Just GT.Corpus) = GT.Annuaire linkNodeType (Just GT.Corpus) = GT.Annuaire
......
...@@ -3,6 +3,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Search where ...@@ -3,6 +3,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Search where
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff) import Effect.Aff (Aff, launchAff)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Store as Store import Gargantext.Components.App.Store as Store
import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar (searchBar) import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar (searchBar)
import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField (defaultSearch) import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField (defaultSearch)
...@@ -68,7 +69,7 @@ actionSearchWithLangsCpt = here.component "actionSearchWithLangs" cpt ...@@ -68,7 +69,7 @@ actionSearchWithLangsCpt = here.component "actionSearchWithLangs" cpt
] ]
where where
searchOn :: (Action -> Aff Unit) searchOn :: (Action -> Aff Unit)
-> GT.AsyncTaskWithType -> GAT.Task
-> Effect Unit -> Effect Unit
searchOn dispatch' task = do searchOn dispatch' task = do
_ <- launchAff $ dispatch' (DoSearch task) _ <- launchAff $ dispatch' (DoSearch task)
......
...@@ -4,6 +4,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar ...@@ -4,6 +4,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar
) where ) where
import Effect (Effect) import Effect (Effect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField (searchField) import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField (searchField)
import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (Search, allDatabases) import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (Search, allDatabases)
import Gargantext.Components.Lang (Lang) import Gargantext.Components.Lang (Lang)
...@@ -21,7 +22,7 @@ here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar" ...@@ -21,7 +22,7 @@ here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar"
type Props = ( errors :: T.Box (Array FrontendError) type Props = ( errors :: T.Box (Array FrontendError)
, langs :: Array Lang , langs :: Array Lang
, onSearch :: GT.AsyncTaskWithType -> Effect Unit , onSearch :: GAT.Task -> Effect Unit
, search :: T.Box Search , search :: T.Box Search
, session :: Session , session :: Session
) )
......
...@@ -13,6 +13,7 @@ import Data.Tuple (Tuple(..)) ...@@ -13,6 +13,7 @@ import Data.Tuple (Tuple(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (launchAff_) import Effect.Aff (launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Action.Search.Frame (searchIframes) import Gargantext.Components.Forest.Tree.Node.Action.Search.Frame (searchIframes)
import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (DataField(..), Database(..), IMT_org(..), Org(..), SearchQuery(..), allOrgs, dataFields, defaultSearchQuery, doc, performSearch, datafield2database, Search, dbFromInputValue, dbToInputValue) import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (DataField(..), Database(..), IMT_org(..), Org(..), SearchQuery(..), allOrgs, dataFields, defaultSearchQuery, doc, performSearch, datafield2database, Search, dbFromInputValue, dbToInputValue)
import Gargantext.Components.GraphQL.Endpoints (getIMTSchools, getUser) import Gargantext.Components.GraphQL.Endpoints (getIMTSchools, getUser)
...@@ -53,7 +54,7 @@ type Props = ...@@ -53,7 +54,7 @@ type Props =
, errors :: T.Box (Array FrontendError) , errors :: T.Box (Array FrontendError)
, langs :: Array Lang , langs :: Array Lang
-- State hook for a search, how we get data in and out -- State hook for a search, how we get data in and out
, onSearch :: GT.AsyncTaskWithType -> Effect Unit , onSearch :: GAT.Task -> Effect Unit
, search :: T.Box Search , search :: T.Box Search
, session :: Session , session :: Session
) )
...@@ -645,7 +646,7 @@ searchInputCpt = here.component "searchInput" cpt ...@@ -645,7 +646,7 @@ searchInputCpt = here.component "searchInput" cpt
type SubmitButtonProps = type SubmitButtonProps =
( errors :: T.Box (Array FrontendError) ( errors :: T.Box (Array FrontendError)
, onSearch :: GT.AsyncTaskWithType -> Effect Unit , onSearch :: GAT.Task -> Effect Unit
, search :: T.Box Search , search :: T.Box Search
, selection :: T.Box ListSelection.Selection , selection :: T.Box ListSelection.Selection
, session :: Session , session :: Session
...@@ -677,13 +678,13 @@ submitButtonComponent = here.component "submitButton" cpt ...@@ -677,13 +678,13 @@ submitButtonComponent = here.component "submitButton" cpt
type TriggerSearch = type TriggerSearch =
( errors :: T.Box (Array FrontendError) ( errors :: T.Box (Array FrontendError)
, onSearch :: GT.AsyncTaskWithType -> Effect Unit , onSearch :: GAT.Task -> Effect Unit
, search :: T.Box Search , search :: T.Box Search
, selection :: T.Box ListSelection.Selection , selection :: T.Box ListSelection.Selection
, session :: Session , session :: Session
) )
triggerSearch :: { onSearch :: (GT.AsyncTaskWithType -> Effect Unit) triggerSearch :: { onSearch :: (GAT.Task -> Effect Unit)
, errors :: T.Box (Array FrontendError) , errors :: T.Box (Array FrontendError)
, session :: Session , session :: Session
, selection :: ListSelection.Selection , selection :: ListSelection.Selection
......
...@@ -12,6 +12,7 @@ import Data.Set (Set) ...@@ -12,6 +12,7 @@ import Data.Set (Set)
import Data.Set as Set import Data.Set as Set
import Data.Tuple (Tuple) import Data.Tuple (Tuple)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.GraphQL.IMT as GQLIMT import Gargantext.Components.GraphQL.IMT as GQLIMT
import Gargantext.Components.Lang (Lang) import Gargantext.Components.Lang (Lang)
import Gargantext.Components.ListSelection.Types as ListSelection import Gargantext.Components.ListSelection.Types as ListSelection
...@@ -339,9 +340,10 @@ defaultSearchQuery = SearchQuery ...@@ -339,9 +340,10 @@ defaultSearchQuery = SearchQuery
, selection : ListSelection.NoList -- MyListsFirst , selection : ListSelection.NoList -- MyListsFirst
} }
performSearch :: Session -> Int -> SearchQuery -> AffRESTError GT.AsyncTaskWithType performSearch :: Session -> Int -> SearchQuery -> AffRESTError GAT.Task
performSearch session nodeId q = do performSearch session nodeId q = do
eTask :: Either RESTError GT.AsyncTask <- post session p q post session p q
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.Query }) <$> eTask -- eTask :: Either RESTError GT.AsyncTask <- post session p q
-- pure $ (\task -> GAT.Task { task, typ: GT.Query }) <$> eTask
where where
p = GR.NodeAPI GT.Corpus (Just nodeId) $ GT.asyncTaskTypePath GT.Query p = GR.NodeAPI GT.Corpus (Just nodeId) $ GT.asyncTaskTypePath GT.Query
...@@ -2,6 +2,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Types where ...@@ -2,6 +2,7 @@ module Gargantext.Components.Forest.Tree.Node.Action.Types where
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe) import Data.Maybe (Maybe)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Action.Contact.Types (AddContactParams) import Gargantext.Components.Forest.Tree.Node.Action.Contact.Types (AddContactParams)
import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (UpdateNodeParams) import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (UpdateNodeParams)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat, FileType, UploadFileBlob) import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat, FileType, UploadFileBlob)
...@@ -15,7 +16,7 @@ data Action = AddNode String GT.NodeType ...@@ -15,7 +16,7 @@ data Action = AddNode String GT.NodeType
| DeleteNode GT.NodeType | DeleteNode GT.NodeType
| RenameNode String | RenameNode String
| UpdateNode UpdateNodeParams | UpdateNode UpdateNodeParams
| DoSearch GT.AsyncTaskWithType | DoSearch GAT.Task
| UploadFile GT.NodeType FileType FileFormat Lang (Maybe String) String Selection | UploadFile GT.NodeType FileType FileFormat Lang (Maybe String) String Selection
| UploadArbitraryFile FileFormat (Maybe String) UploadFileBlob | UploadArbitraryFile FileFormat (Maybe String) UploadFileBlob
| UploadFrameCalc Lang Selection | UploadFrameCalc Lang Selection
......
...@@ -9,6 +9,7 @@ import Data.Either (Either(..)) ...@@ -9,6 +9,7 @@ import Data.Either (Either(..))
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..)) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools as Tools import Gargantext.Components.Forest.Tree.Node.Tools as Tools
...@@ -29,14 +30,11 @@ import Toestand as T ...@@ -29,14 +30,11 @@ import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Update" here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Update"
updateRequest :: UpdateNodeParams -> Session -> ID -> AffRESTError GT.AsyncTaskWithType updateRequest :: UpdateNodeParams -> Session -> ID -> AffRESTError GAT.Task
updateRequest updateNodeParams session nodeId = do updateRequest updateNodeParams session nodeId = do
eTask :: Either RESTError GT.AsyncTask <- post session p updateNodeParams post session p updateNodeParams
case eTask of where
Left err -> pure $ Left err p = GR.NodeAPI GT.Node (Just nodeId) "update"
Right task -> pure $ Right $ GT.AsyncTaskWithType { task, typ: GT.UpdateNode }
where
p = GR.NodeAPI GT.Node (Just nodeId) "update"
---------------------------------------------------------------------- ----------------------------------------------------------------------
type UpdateProps = type UpdateProps =
......
...@@ -14,6 +14,7 @@ import Data.Tuple.Nested ((/\)) ...@@ -14,6 +14,7 @@ import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff) import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Forest.Tree.Node.Action (Props) import Gargantext.Components.Forest.Tree.Node.Action (Props)
...@@ -554,21 +555,10 @@ uploadFile :: { contents :: String ...@@ -554,21 +555,10 @@ uploadFile :: { contents :: String
, mName :: Maybe String , mName :: Maybe String
, selection :: ListSelection.Selection , selection :: ListSelection.Selection
, session :: Session } , session :: Session }
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
{-
uploadFile session NodeList id JSON { mName, contents } = do
let url = GR.NodeAPI NodeList (Just id) $ GT.asyncTaskTypePath GT.ListUpload
-- { input: { data: ..., filetype: "JSON", name: "..." } }
let body = { input: { data: contents
, filetype: "JSON"
, name: fromMaybe "" mName } }
task <- post session url body
pure $ GT.AsyncTaskWithType { task, typ: GT.Form }
-}
uploadFile { contents, fileFormat, lang, fileType, id, nodeType, mName, selection, session } = do uploadFile { contents, fileFormat, lang, fileType, id, nodeType, mName, selection, session } = do
-- contents <- readAsText blob -- contents <- readAsText blob
eTask :: Either RESTError GT.AsyncTask <- postWwwUrlencoded session p body postWwwUrlencoded session p body
pure $ (\task -> GT.AsyncTaskWithType { task, typ }) <$> eTask
--postMultipartFormData session p fileContents --postMultipartFormData session p fileContents
where where
bodyParams = [ Tuple "_wf_data" (Just contents) bodyParams = [ Tuple "_wf_data" (Just contents)
...@@ -603,7 +593,7 @@ uploadFile { contents, fileFormat, lang, fileType, id, nodeType, mName, selectio ...@@ -603,7 +593,7 @@ uploadFile { contents, fileFormat, lang, fileType, id, nodeType, mName, selectio
uploadArbitraryFile :: Session uploadArbitraryFile :: Session
-> ID -> ID
-> {blob :: UploadFileBlob, fileFormat :: FileFormat, mName :: Maybe String} -> {blob :: UploadFileBlob, fileFormat :: FileFormat, mName :: Maybe String}
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
uploadArbitraryFile session id { fileFormat, mName, blob: UploadFileBlob blob } = do uploadArbitraryFile session id { fileFormat, mName, blob: UploadFileBlob blob } = do
contents <- readAsDataURL blob contents <- readAsDataURL blob
uploadArbitraryData session id fileFormat mName contents uploadArbitraryData session id fileFormat mName contents
...@@ -613,12 +603,11 @@ uploadArbitraryData :: Session ...@@ -613,12 +603,11 @@ uploadArbitraryData :: Session
-> FileFormat -> FileFormat
-> Maybe String -> Maybe String
-> String -> String
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
uploadArbitraryData session id fileFormat mName contents' = do uploadArbitraryData session id fileFormat mName contents' = do
let re = fromRight' (\_ -> unsafeCrashWith "Unexpected Left") $ DSR.regex "data:.*;base64," DSRF.noFlags let re = fromRight' (\_ -> unsafeCrashWith "Unexpected Left") $ DSR.regex "data:.*;base64," DSRF.noFlags
contents = DSR.replace re "" contents' contents = DSR.replace re "" contents'
eTask :: Either RESTError GT.AsyncTask <- postWwwUrlencoded session p (bodyParams contents) postWwwUrlencoded session p (bodyParams contents)
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.UploadFile }) <$> eTask
where where
p = GR.NodeAPI GT.Node (Just id) $ GT.asyncTaskTypePath GT.UploadFile p = GR.NodeAPI GT.Node (Just id) $ GT.asyncTaskTypePath GT.UploadFile
...@@ -818,10 +807,9 @@ uploadFrameCalc :: Session ...@@ -818,10 +807,9 @@ uploadFrameCalc :: Session
-> ID -> ID
-> Lang -> Lang
-> ListSelection.Selection -> ListSelection.Selection
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
uploadFrameCalc session id lang selection = do uploadFrameCalc session id lang selection = do
let p = GR.NodeAPI GT.Node (Just id) $ GT.asyncTaskTypePath GT.UploadFrameCalc let p = GR.NodeAPI GT.Node (Just id) $ GT.asyncTaskTypePath GT.UploadFrameCalc
eTask <- post session p { _wf_lang: Just lang post session p { _wf_lang: Just lang
, _wf_selection: selection } , _wf_selection: selection }
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.UploadFrameCalc }) <$> eTask
...@@ -4,6 +4,7 @@ import Data.Either (Either) ...@@ -4,6 +4,7 @@ import Data.Either (Either)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..)) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Utils (loadLanguages) import Gargantext.Components.Forest.Tree.Node.Action.Utils (loadLanguages)
...@@ -152,8 +153,6 @@ type Params = ...@@ -152,8 +153,6 @@ type Params =
, selection :: ListSelection.Selection , selection :: ListSelection.Selection
) )
documentsFromWriteNodesReq :: Session -> Record Params -> AffRESTError GT.AsyncTaskWithType documentsFromWriteNodesReq :: Session -> Record Params -> AffRESTError GAT.Task
documentsFromWriteNodesReq session params@{ id } = do documentsFromWriteNodesReq session params@{ id } = do
eTask :: Either RESTError GT.AsyncTask <- post session (NodeAPI GT.Node (Just id) "documents-from-write-nodes") params
post session (NodeAPI GT.Node (Just id) "documents-from-write-nodes") params
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.UpdateNode }) <$> eTask
module Gargantext.Components.Forest.Tree.Node.Tools.FTree where module Gargantext.Components.Forest.Tree.Node.Tools.FTree where
import Data.Generic.Rep (class Generic)
import Data.Eq.Generic (genericEq) import Data.Eq.Generic (genericEq)
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype) import Data.Newtype (class Newtype)
import Simple.JSON as JSON import Gargantext.AsyncTasks as GAT
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Types as GT import Gargantext.Types as GT
import Simple.JSON as JSON
----------------------------------------------------------------------- -----------------------------------------------------------------------
type ID = Int type ID = Int
...@@ -26,7 +25,7 @@ instance Eq a => Eq (NTree a) where ...@@ -26,7 +25,7 @@ instance Eq a => Eq (NTree a) where
eq (NTree a1 as1) (NTree a2 as2) = (eq a1 a2) && (eq as1 as2) eq (NTree a1 as1) (NTree a2 as2) = (eq a1 a2) && (eq as1 as2)
type Tree = { tree :: FTree type Tree = { tree :: FTree
, tasks :: Array GT.AsyncTaskWithType , tasks :: Array GAT.Task
} }
fTreeID :: FTree -> ID fTreeID :: FTree -> ID
......
module Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar where
import Data.Int (floor)
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Effect.Timer (clearInterval, setInterval)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Config.Utils (handleErrorInAsyncProgress, handleRESTError)
import Gargantext.Prelude
import Gargantext.Routes (SessionRoute(..))
import Gargantext.Sessions (Session, get)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record.Extra as RX
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar"
-- data BarType = Bar | Pie
-- type Props = (
-- asyncTask :: GT.AsyncTaskWithType
-- , barType :: BarType
-- , nodeId :: GT.ID
-- , onFinish :: Unit -> Effect Unit
-- , session :: Session
-- )
-- asyncProgressBar :: R2.Component Props
-- asyncProgressBar = R.createElement asyncProgressBarCpt
-- asyncProgressBarCpt :: R.Component Props
-- asyncProgressBarCpt = R2.hereComponent here "asyncProgressBar" hCpt where
-- hCpt hp props@{ asyncTask: (GT.AsyncTaskWithType {task: GT.AsyncTask {id}})
-- , barType
-- , onFinish
-- } _ = do
-- { errors } <- AppStore.use
-- progress <- T.useBox 0.0
-- intervalIdRef <- R.useRef Nothing
-- R.useEffectOnce' $ do
-- intervalId <- setInterval 1000 $ do
-- launchAff_ $ do
-- let rdata = (RX.pick props :: Record QueryProgressData)
-- eAsyncProgress <- queryProgress rdata
-- handleRESTError hp errors eAsyncProgress $
-- \asyncProgress -> liftEffect $ do
-- let GT.AsyncProgress { status } = asyncProgress
-- T.write_ (min 100.0 $ GT.progressPercent asyncProgress) progress
-- if (status == GT.IsFinished) || (status == GT.IsKilled) || (status == GT.IsFailure) then do
-- _ <- case R.readRef intervalIdRef of
-- Nothing -> pure unit
-- Just iid -> clearInterval iid
-- handleErrorInAsyncProgress errors asyncProgress
-- onFinish unit
-- else
-- pure unit
-- R.setRef intervalIdRef $ Just intervalId
-- pure unit
-- pure $ progressIndicator { barType, label: id, progress }
--------------------------------------------------------------
-- type ProgressIndicatorProps =
-- ( barType :: BarType
-- , label :: String
-- , progress :: T.Box Number
-- )
-- progressIndicator :: Record ProgressIndicatorProps -> R.Element
-- progressIndicator p = R.createElement progressIndicatorCpt p []
-- progressIndicatorCpt :: R.Component ProgressIndicatorProps
-- progressIndicatorCpt = here.component "progressIndicator" cpt
-- where
-- cpt { barType, progress } _ = do
-- progress' <- T.useLive T.unequal progress
-- let progressInt = floor progress'
-- case barType of
-- Bar -> pure $
-- H.div { className: "progress" }
-- [ H.div { className: "progress-bar"
-- , role: "progressbar"
-- , style: { width: (show $ progressInt) <> "%" }
-- } [ ]
-- ]
-- Pie -> pure $
-- H.div { className: "progress-pie" }
-- [ H.div { className: "progress-pie-segment"
-- , style: { "--over50": if progressInt < 50 then "0" else "1"
-- , "--value": show $ progressInt } } [
-- ]
-- ]
--------------------------------------------------------------
type QueryProgressData =
( asyncTask :: GT.AsyncTaskWithType
, nodeId :: GT.ID
, session :: Session
)
queryProgress :: Record QueryProgressData -> AffRESTError GT.AsyncProgress
queryProgress { asyncTask: GT.AsyncTaskWithType { task: GT.AsyncTask {id}
, typ
}
, nodeId
, session
} = get session (p typ)
where
-- TODO refactor path
p GT.ListCSVUpload = NodeAPI GT.NodeList (Just nodeId) $ GT.asyncTaskTypePath GT.ListCSVUpload <> id <> "/poll?limit=1"
p GT.UpdateNgramsCharts = NodeAPI GT.Node (Just nodeId) $ path <> id <> "/poll?limit=1"
p GT.UpdateNode = NodeAPI GT.Node (Just nodeId) $ path <> id <> "/poll?limit=1"
p _ = NodeAPI GT.Corpus (Just nodeId) $ path <> id <> "/poll?limit=1"
path = GT.asyncTaskTypePath typ
-- TODO wait route: take the result if failure then message
...@@ -3,6 +3,7 @@ module Gargantext.Components.GraphExplorer.API where ...@@ -3,6 +3,7 @@ module Gargantext.Components.GraphExplorer.API where
import Gargantext.Prelude import Gargantext.Prelude
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (AffRESTError) import Gargantext.Config.REST (AffRESTError)
import Gargantext.Core.NgramsTable.Types as CNT import Gargantext.Core.NgramsTable.Types as CNT
...@@ -23,10 +24,9 @@ type GraphAsyncUpdateParams = ...@@ -23,10 +24,9 @@ type GraphAsyncUpdateParams =
, version :: CNT.Version , version :: CNT.Version
) )
graphAsyncUpdate :: Record GraphAsyncUpdateParams -> AffRESTError GT.AsyncTaskWithType graphAsyncUpdate :: Record GraphAsyncUpdateParams -> AffRESTError GAT.Task
graphAsyncUpdate { graphId, listId, nodes, session, termList, version } = do graphAsyncUpdate { graphId, listId, nodes, session, termList, version } = do
eTask <- post session p q post session p q
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.GraphRecompute }) <$> eTask
where where
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute
q = { listId q = { listId
...@@ -40,24 +40,13 @@ type GraphAsyncRecomputeParams = ...@@ -40,24 +40,13 @@ type GraphAsyncRecomputeParams =
, session :: Session , session :: Session
) )
graphAsyncRecompute :: Record GraphAsyncRecomputeParams -> AffRESTError GT.AsyncTaskWithType graphAsyncRecompute :: Record GraphAsyncRecomputeParams -> AffRESTError GAT.Task
graphAsyncRecompute { graphId, session } = do graphAsyncRecompute { graphId, session } = do
eTask <- post session p q post session p q
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.GraphRecompute }) <$> eTask
where where
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute
q = {} q = {}
type QueryProgressParams =
( graphId :: Int
, session :: Session
, taskId :: String
)
queryProgress :: Record QueryProgressParams -> AffRESTError GT.AsyncProgress
queryProgress { graphId, session, taskId } = do
get session $ GR.GraphAPI graphId $ "async/" <> taskId <> "/poll"
type GraphVersions = type GraphVersions =
( gv_graph :: Maybe Int ( gv_graph :: Maybe Int
, gv_repo :: Int , gv_repo :: Int
......
module Gargantext.Components.GraphQL.Task where
import Gargantext.Types (AsyncTaskID)
type AsyncTaskStatus = String
type AsyncTaskType = String
type AsyncTask =
{ id :: AsyncTaskID
, status :: AsyncTaskStatus}
type AsyncTaskWithType =
{ task :: AsyncTask
, typ :: AsyncTaskType }
...@@ -19,6 +19,7 @@ import Gargantext.Hooks.FormValidation (VForm, useFormValidation) ...@@ -19,6 +19,7 @@ import Gargantext.Hooks.FormValidation (VForm, useFormValidation)
import Gargantext.Hooks.FormValidation.Unboxed as FV import Gargantext.Hooks.FormValidation.Unboxed as FV
import Gargantext.Hooks.StateRecord (useStateRecord) import Gargantext.Hooks.StateRecord (useStateRecord)
import Gargantext.Sessions (postForgotPasswordRequest) import Gargantext.Sessions (postForgotPasswordRequest)
import Gargantext.Types as GT
import Gargantext.Utils ((?)) import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
...@@ -195,5 +196,5 @@ formValidation r = foldl append mempty rules ...@@ -195,5 +196,5 @@ formValidation r = foldl append mempty rules
sendEmail :: sendEmail ::
Backend Backend
-> FormData -> FormData
-> Aff (Either String { status :: String }) -> Aff (Either String GT.WorkerTask)
sendEmail backend { email } = postForgotPasswordRequest backend email sendEmail backend { email } = postForgotPasswordRequest backend email
...@@ -51,16 +51,19 @@ removeCallback (State state@{ callbacks }) topic uuid = ...@@ -51,16 +51,19 @@ removeCallback (State state@{ callbacks }) topic uuid =
-- | Execute all callbacks for a given Notification -- | Execute all callbacks for a given Notification
callNotification :: State -> Notification -> Effect Unit callNotification :: State -> Notification -> Effect Unit
callNotification (State { callbacks }) (Notification topic message) = do callNotification (State { callbacks }) n = do
-- here.log2 "[callTopic] topic" topic -- here.log2 "[callTopic] topic" topic
-- here.log2 "[callTopic] callbacks" (HM.values callbacks) -- here.log2 "[callTopic] callbacks" (HM.values callbacks)
-- here.log2 "[callTopic] topicCallbacks" (HM.values topicCallbacks) -- here.log2 "[callTopic] topicCallbacks" (HM.values topicCallbacks)
_ <- for (HM.values topicCallbacks) $ \cb -> do
cb message let topics = notificationTopics n
pure unit
void $ for topics $ \topic -> do
void $ for (HM.values $ topicCallbacks topic) $ \cb -> do
cb n
where where
topicCallbacks :: CallbacksHM topicCallbacks :: Topic -> CallbacksHM
topicCallbacks = fromMaybe HM.empty $ HM.lookup topic callbacks topicCallbacks topic = fromMaybe HM.empty $ HM.lookup topic callbacks
...@@ -174,7 +177,7 @@ connect ws@(WSNotification ws') url session = do ...@@ -174,7 +177,7 @@ connect ws@(WSNotification ws') url session = do
case parsed of case parsed of
Left err -> do Left err -> do
here.log2 "[connect] Can't parse message" err here.log2 "[connect] Can't parse message" err
Right n@(Notification topic _message) -> do Right n -> do
-- here.log2 "[connect] notification" topic -- here.log2 "[connect] notification" topic
performAction ws (Call n) performAction ws (Call n)
-- Right parsed' -> do -- Right parsed' -> do
......
...@@ -37,7 +37,7 @@ type NodeId = Int ...@@ -37,7 +37,7 @@ type NodeId = Int
type UUID = String type UUID = String
data Topic = data Topic =
UpdateJobProgress GT.AsyncTaskID UpdateWorkerProgress GT.WorkerTask
| UpdateTree NodeId | UpdateTree NodeId
derive instance Generic Topic _ derive instance Generic Topic _
instance Eq Topic where eq = genericEq instance Eq Topic where eq = genericEq
...@@ -48,16 +48,16 @@ instance JSON.ReadForeign Topic where ...@@ -48,16 +48,16 @@ instance JSON.ReadForeign Topic where
readImpl f = do readImpl f = do
{ type: type_ } <- JSON.readImpl f :: F.F { type :: String } { type: type_ } <- JSON.readImpl f :: F.F { type :: String }
case type_ of case type_ of
"update_job_progress" -> do "update_worker_progress" -> do
{ j_id } <- JSON.readImpl f :: F.F { j_id :: GT.AsyncTaskID } { ji } <- JSON.readImpl f :: F.F { ji :: GT.WorkerTask }
pure $ UpdateJobProgress j_id pure $ UpdateWorkerProgress ji
"update_tree" -> do "update_tree" -> do
{ node_id } <- JSON.readImpl f :: F.F { node_id :: NodeId } { node_id } <- JSON.readImpl f :: F.F { node_id :: NodeId }
pure $ UpdateTree node_id pure $ UpdateTree node_id
s -> F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unknown Topic type: " <> s s -> F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unknown Topic type: " <> s
instance JSON.WriteForeign Topic where instance JSON.WriteForeign Topic where
writeImpl (UpdateJobProgress j_id) = JSON.writeImpl { "type": "update_job_progress" writeImpl (UpdateWorkerProgress ji) = JSON.writeImpl { "type": "update_worker_progress"
, j_id } , ji }
writeImpl (UpdateTree node_id) = JSON.writeImpl { "type": "update_tree" writeImpl (UpdateTree node_id) = JSON.writeImpl { "type": "update_tree"
, node_id } , node_id }
...@@ -78,42 +78,36 @@ instance JSON.WriteForeign WSRequest where ...@@ -78,42 +78,36 @@ instance JSON.WriteForeign WSRequest where
writeImpl WSDeauthorize = JSON.writeImpl { request: "deauthorize" } writeImpl WSDeauthorize = JSON.writeImpl { request: "deauthorize" }
data Message =
-- TODO
-- MJobProgress GT.AsyncProgress
-- MJobProgress GT.AsyncTaskLog
MJobProgress GT.AsyncProgress
| MEmpty
derive instance Generic Message _
instance JSON.ReadForeign Message where
readImpl f = do
{ type: type_ } <- JSON.readImpl f :: F.F { type :: String }
case type_ of
"MJobProgress" -> do
-- TODO
-- { job_progress } <- JSON.readImpl f :: F.F { job_progress :: GT.AsyncProgress }
-- { job_progress } <- JSON.readImpl f :: F.F { job_progress :: GT.AsyncTaskLog }
{ job_status } <- JSON.readImpl f :: F.F { job_status :: GT.AsyncProgress }
pure $ MJobProgress job_status
"MEmpty" -> do
pure MEmpty
s -> do F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unknown Message type: " <> s
data Notification = data Notification =
Notification Topic Message NUpdateWorkerProgress GT.WorkerTask GT.AsyncTaskLog
| NUpdateTree NodeId
derive instance Generic Notification _ derive instance Generic Notification _
instance JSON.ReadForeign Notification where instance JSON.ReadForeign Notification where
readImpl f = do readImpl f = do
let str = JSON.read_ f :: Maybe String { type: type_ } <- JSON.readImpl f :: F.F { type :: String }
case str of case type_ of
Nothing -> do "update_worker_progress" -> do
{ notification } <- JSON.readImpl f :: F.F { notification :: { topic :: Topic, message :: Message } } { job_info, job_log } <- JSON.readImpl f :: F.F { job_info :: GT.WorkerTask, job_log :: GT.AsyncTaskLog }
pure $ Notification notification.topic notification.message pure $ NUpdateWorkerProgress job_info job_log
Just s -> F.fail $ F.ErrorAtProperty "_" $ F.ForeignError $ "unkown string: " <> s "update_tree" -> do
{ node_id } <- JSON.readImpl f :: F.F { node_id :: NodeId }
pure $ NUpdateTree node_id
type Callback = Message -> Effect Unit s -> F.fail $ F.ErrorAtProperty "type" $ F.ForeignError $ "unkown type: " <> s
notificationTopics :: Notification -> Array Topic
notificationTopics (NUpdateWorkerProgress workerTask@(GT.WorkerTask { node_id }) _) =
[ UpdateWorkerProgress workerTask ] <> updateTree
where
-- when receiving a worker progress notification, we are also
-- interested in 'update tree' subscriptions, because there might
-- be a new job that we didn't subscribe to
updateTree = case node_id of
Nothing -> []
Just nId -> [ UpdateTree nId ]
notificationTopics (NUpdateTree nodeId) = [ UpdateTree nodeId ]
type Callback = Notification -> Effect Unit
type CallbacksHM = HM.HashMap UUID Callback type CallbacksHM = HM.HashMap UUID Callback
......
...@@ -5,7 +5,7 @@ module Gargantext.Components.PhyloExplorer.API ...@@ -5,7 +5,7 @@ module Gargantext.Components.PhyloExplorer.API
, Clique(..), ReflexiveClique(..), CliqueFilter(..) , Clique(..), ReflexiveClique(..), CliqueFilter(..)
, toReflexiveTimeUnit, fromReflexiveTimeUnit, extractCriteria , toReflexiveTimeUnit, fromReflexiveTimeUnit, extractCriteria
, toReflexiveClique , toReflexiveClique
, update, updateProgress , update
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -15,6 +15,7 @@ import Data.Generic.Rep (class Generic) ...@@ -15,6 +15,7 @@ import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype) import Data.Newtype (class Newtype)
import Data.Show.Generic (genericShow) import Data.Show.Generic (genericShow)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.PhyloExplorer.JSON (PhyloJSON) import Gargantext.Components.PhyloExplorer.JSON (PhyloJSON)
import Gargantext.Components.PhyloExplorer.Types (PhyloSet, parseToPhyloSet) import Gargantext.Components.PhyloExplorer.Types (PhyloSet, parseToPhyloSet)
import Gargantext.Config.REST (AffRESTError) import Gargantext.Config.REST (AffRESTError)
...@@ -306,37 +307,10 @@ update :: ...@@ -306,37 +307,10 @@ update ::
Session Session
-> NodeID -> NodeID
-> Unit -> Unit
-> AffRESTError GT.AsyncTaskWithType -> AffRESTError GAT.Task
update session nodeId _ update session nodeId _
= S.post session request {} = S.post session request {}
>>= case _ of
Left err -> pure $ Left err
Right task -> pure $ Right $ GT.AsyncTaskWithType
{ task
, typ: GT.UpdateNode
}
where where
request = GR.NodeAPI GT.Node (Just nodeId) request = GR.NodeAPI GT.Node (Just nodeId)
(GT.asyncTaskTypePath GT.UpdateNode) (GT.asyncTaskTypePath GT.UpdateNode)
updateProgress ::
Session
-> NodeID
-> GT.AsyncTaskWithType
-> AffRESTError GT.AsyncProgress
updateProgress
session
nodeId
(GT.AsyncTaskWithType { task: GT.AsyncTask { id } })
=
S.get session request
where
request = GR.NodeAPI GT.Node (Just nodeId)
(GT.asyncTaskTypePath GT.UpdateNode <> pollParams)
pollParams = "/" <> id <> "/poll?limit1"
...@@ -5,12 +5,12 @@ import Gargantext.Prelude ...@@ -5,12 +5,12 @@ import Gargantext.Prelude
import Data.Array as A import Data.Array as A
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Foldable (foldl) import Data.Foldable (foldl)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe, Maybe(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.Config.REST (RESTError, logRESTError) import Gargantext.Config.REST (RESTError, logRESTError)
import Gargantext.Types (AsyncEvent(..), AsyncProgress(..), AsyncTaskLog(..), AsyncTaskStatus(..), FrontendError(..)) import Gargantext.Types (AsyncEvent(..), AsyncTaskLog(..), FrontendError(..), asyncTaskLogEventsErrorMessage)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Toestand as T import Toestand as T
...@@ -29,23 +29,31 @@ handleRESTError herePrefix errors (Left error) _ = liftEffect $ do ...@@ -29,23 +29,31 @@ handleRESTError herePrefix errors (Left error) _ = liftEffect $ do
-- here.warn2 "[handleTaskError] RESTError" error -- here.warn2 "[handleTaskError] RESTError" error
handleRESTError _ _ (Right task) handler = handler task handleRESTError _ _ (Right task) handler = handler task
handleErrorInAsyncProgress :: T.Box (Array FrontendError) -- handleErrorInAsyncProgress :: T.Box (Array FrontendError)
-> AsyncProgress -- -> AsyncProgress
-> Effect Unit -- -> Effect Unit
handleErrorInAsyncProgress errors ap@(AsyncProgress { status: IsFailure }) = do -- handleErrorInAsyncProgress errors ap@(AsyncProgress { status: IsFailure }) = do
T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors -- T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors
handleErrorInAsyncProgress errors ap@(AsyncProgress { log, status: IsFinished }) = do -- handleErrorInAsyncProgress errors ap@(AsyncProgress { log, status: IsFinished }) = do
if countFailed > 0 then -- if countFailed > 0 then
T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors -- T.modify_ (A.cons $ FStringError { error: concatErrors ap }) errors
else -- else
pure unit -- pure unit
where -- where
countFailed = foldl (+) 0 $ (\(AsyncTaskLog { failed }) -> failed) <$> log -- countFailed = foldl (+) 0 $ (\(AsyncTaskLog { failed }) -> failed) <$> log
handleErrorInAsyncProgress _ _ = pure unit -- handleErrorInAsyncProgress _ _ = pure unit
concatErrors :: AsyncProgress -> String -- concatErrors :: AsyncProgress -> String
concatErrors (AsyncProgress { error, log }) = foldl eventsErrorMessage (fromMaybe "" error) log -- concatErrors (AsyncProgress { error, log }) = foldl eventsErrorMessage (fromMaybe "" error) log
where -- where
eventsErrorMessage acc (AsyncTaskLog { events }) = (foldl eventErrorMessage "" events) <> "\n" <> acc -- eventsErrorMessage acc atl = asyncTaskLogEventsErrorMessage atl <> "\n" <> acc
eventErrorMessage acc (AsyncEvent { level: "ERROR", message }) = message <> "\n" <> acc
eventErrorMessage acc _ = acc
handleErrorInAsyncTaskLog :: T.Box (Array FrontendError)
-> AsyncTaskLog
-> Effect Unit
handleErrorInAsyncTaskLog errors atl =
case asyncTaskLogEventsErrorMessage atl of
Nothing -> pure unit
Just error ->
T.modify_ (A.cons $ FStringError { error }) errors
...@@ -5,6 +5,7 @@ module Gargantext.Context.Progress ...@@ -5,6 +5,7 @@ module Gargantext.Context.Progress
) where ) where
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Foldable (foldl)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
...@@ -13,14 +14,12 @@ import Effect.Class (liftEffect) ...@@ -13,14 +14,12 @@ import Effect.Class (liftEffect)
import Effect.Timer (IntervalId, clearInterval, setInterval) import Effect.Timer (IntervalId, clearInterval, setInterval)
import Gargantext.AsyncTasks as GAT import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Store as AppStore import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (QueryProgressData, queryProgress)
import Gargantext.Components.Notifications as Notifications import Gargantext.Components.Notifications as Notifications
import Gargantext.Components.Notifications.Types as NotificationsT import Gargantext.Components.Notifications.Types as NT
import Gargantext.Config.Utils (handleErrorInAsyncProgress, handleRESTError) import Gargantext.Config.Utils (handleErrorInAsyncTaskLog, handleRESTError)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types (AsyncProgress)
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
...@@ -29,7 +28,7 @@ import Toestand as T ...@@ -29,7 +28,7 @@ import Toestand as T
type AsyncProps = type AsyncProps =
( asyncTask :: GT.AsyncTaskWithType ( asyncTask :: GAT.Task
, nodeId :: GT.ID , nodeId :: GT.ID
, onFinish :: Unit -> Effect Unit , onFinish :: Unit -> Effect Unit
, session :: Session , session :: Session
...@@ -60,89 +59,52 @@ asyncProgressCpt = R2.hereComponent here "asyncProgress" hCpt where ...@@ -60,89 +59,52 @@ asyncProgressCpt = R2.hereComponent here "asyncProgress" hCpt where
interval <- T.useBox 1000 interval <- T.useBox 1000
-- Methods -- Methods
let let
-- TODO Manage somehow to get the whole job status sent here via onProgress :: GT.AsyncTaskLog -> Aff Unit
-- websockets, then we can remove the 'Maybe' onProgress atl@(GT.AsyncTaskLog log) = liftEffect $ do
fetchJobProgress :: Effect Unit
fetchJobProgress = launchAff_ do T.write_ (min 100.0 $ GT.asyncTaskLogPercent atl) progressBox
let rdata = (RX.pick props :: Record QueryProgressData)
here.log "[onProgress] TODO: Implement status killed"
eAsyncProgress <- queryProgress rdata if GT.asyncTaskLogIsFinished atl ||
-- liftEffect $ here.log2 "[progress] received asyncProgress" eAsyncProgress GT.asyncTaskLogIsError atl
-- exponential backoff in case of errors
-- liftEffect $ do
-- case eAsyncProgress of
-- Left _ -> T.modify_ (_ * 2) interval
-- Right _ -> T.write_ 1000 interval
-- interval' <- T.read interval
-- resetInterval intervalIdRef (Just interval') exec
-- Handle removal of task in case of 500 error (e.g. server
-- was restarted and task id is not found anymore).
-- Error logging will be done below, in handleRESTError
case eAsyncProgress of
Right _ -> pure unit
Left err -> do
liftEffect $ do
resetInterval intervalIdRef Nothing (pure unit)
GAT.finish props.nodeId props.asyncTask tasks
handleRESTError hp errors eAsyncProgress onProgress
-- TODO Ideally we should use this function
-- onProgress jobProgress = do
-- launchAff_ $ onProgress jobProgress
onProgress :: AsyncProgress -> Aff Unit
onProgress value@(GT.AsyncProgress { status }) = liftEffect do
T.write_ (min 100.0 $ GT.progressPercent value) progressBox
if (status == GT.IsFinished) ||
(status == GT.IsKilled) ||
(status == GT.IsFailure)
then do then do
-- resetInterval intervalIdRef Nothing exec handleErrorInAsyncTaskLog errors atl
-- case R.readRef intervalIdRef of -- resetInterval intervalIdRef Nothing (pure unit)
-- Nothing -> R.nothing
-- Just iid -> clearInterval iid
handleErrorInAsyncProgress errors value
resetInterval intervalIdRef Nothing (pure unit)
onFinish unit onFinish unit
else else
R.nothing R.nothing
-- if (status == GT.IsFinished) ||
-- (status == GT.IsKilled) ||
-- (status == GT.IsFailure)
-- then do
-- -- resetInterval intervalIdRef Nothing exec
-- -- case R.readRef intervalIdRef of
-- -- Nothing -> R.nothing
-- -- Just iid -> clearInterval iid
-- handleErrorInAsyncProgress errors value
-- resetInterval intervalIdRef Nothing (pure unit)
-- onFinish unit
-- else
-- R.nothing
useFirstEffect' $ do useFirstEffect' $ do
let (GT.AsyncTaskWithType { task: GT.AsyncTask { id: taskId } }) = props.asyncTask let (GT.WorkerTask { message_id }) = props.asyncTask
let cb msg = do let cb n = do
-- here.log2 "callback! for job update" taskId -- here.log2 "callback! for job update" taskId
case msg of case n of
NotificationsT.MJobProgress jobProgress -> launchAff_ $ onProgress jobProgress NT.NUpdateWorkerProgress _wt jobProgress -> launchAff_ $ onProgress jobProgress
NotificationsT.MEmpty -> fetchJobProgress _ -> pure unit
resetInterval intervalIdRef (Just defaultJobPollInterval) fetchJobProgress -- resetInterval intervalIdRef (Just defaultJobPollInterval) fetchJobProgress
-- The modal window has some problems closing when we refresh too early. This is a HACK -- The modal window has some problems closing when we refresh too early. This is a HACK
-- void $ setTimeout 400 $ T2.reload reload -- void $ setTimeout 400 $ T2.reload reload
let action = NotificationsT.InsertCallback (NotificationsT.UpdateJobProgress taskId) ("task-" <> show taskId) cb
ws <- T.read wsNotification ws <- T.read wsNotification
let action = NT.InsertCallback (NT.UpdateWorkerProgress props.asyncTask) ("task-" <> show message_id) cb
Notifications.performAction ws action Notifications.performAction ws action
fetchJobProgress
-- Hooks
-- useFirstEffect' do
-- resetInterval intervalIdRef (Just 1000) exec
-- intervalId <- setInterval interval' $ exec unit
-- R.setRef intervalIdRef $ Just intervalId
-- TODO Current backend job implementation is that it cannot, by
-- itself, notify us when a job finished. Hence, we are forced to
-- poll for job still. However, we will keep canceling the timer
-- unless there is no progress report for some time.
useFirstEffect' $ do
resetInterval intervalIdRef (Just defaultJobPollInterval) fetchJobProgress
-- Render -- Render
pure $ pure $
......
...@@ -44,7 +44,7 @@ import Gargantext.Config.Utils (handleRESTError) ...@@ -44,7 +44,7 @@ import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Core.NgramsTable.Types import Gargantext.Core.NgramsTable.Types
import Gargantext.Routes (SessionRoute(..)) import Gargantext.Routes (SessionRoute(..))
import Gargantext.Sessions (Session, get, post, put) import Gargantext.Sessions (Session, get, post, put)
import Gargantext.Types (AsyncTask, AsyncTaskType(..), AsyncTaskWithType(..), CTabNgramType(..), FrontendError, OrderBy(..), ScoreType(..), TabSubType(..), TabType(..), TermList(..), TermSize(..)) import Gargantext.Types (CTabNgramType(..), FrontendError, OrderBy(..), ScoreType(..), TabSubType(..), TabType(..), TermList(..), TermSize(..))
import Gargantext.Utils.Either (eitherMap) import Gargantext.Utils.Either (eitherMap)
--import Gargantext.Utils.KarpRabin (indicesOfAny) --import Gargantext.Utils.KarpRabin (indicesOfAny)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
...@@ -584,12 +584,13 @@ chartsAfterSync :: forall props discard. ...@@ -584,12 +584,13 @@ chartsAfterSync :: forall props discard.
chartsAfterSync path'@{ nodeId } errors tasks _ = do chartsAfterSync path'@{ nodeId } errors tasks _ = do
eTask <- postNgramsChartsAsync path' eTask <- postNgramsChartsAsync path'
handleRESTError (R2.herePrefix here "[chartsAfterSync]") errors eTask $ \task -> liftEffect $ do handleRESTError (R2.herePrefix here "[chartsAfterSync]") errors eTask $ \task -> liftEffect $ do
GAT.insert nodeId task tasks -- GAT.insert nodeId task tasks
here.log "[chartsAfterSync] TODO: IMPLEMENT ME!"
postNgramsChartsAsync :: forall s. CoreParams s -> AffRESTError AsyncTaskWithType
postNgramsChartsAsync :: forall s. CoreParams s -> AffRESTError GAT.Task
postNgramsChartsAsync { listIds, nodeId, session, tabType } = do postNgramsChartsAsync { listIds, nodeId, session, tabType } = do
eTask :: Either RESTError AsyncTask <- post session putNgramsAsync acu post session putNgramsAsync acu
pure $ (\task -> AsyncTaskWithType { task, typ: UpdateNgramsCharts }) <$> eTask
where where
acu = AsyncNgramsChartsUpdate { listId: head listIds acu = AsyncNgramsChartsUpdate { listId: head listIds
, tabType } , tabType }
......
...@@ -21,6 +21,7 @@ import Gargantext.Config.REST as REST ...@@ -21,6 +21,7 @@ import Gargantext.Config.REST as REST
import Gargantext.Ends (class ToUrl, Backend, toUrl) import Gargantext.Ends (class ToUrl, Backend, toUrl)
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Sessions.Types (Session(..), Sessions(..), OpenNodes, NodeId, mkNodeId, sessionUrl, sessionId, empty, null, unSessions, lookup, cons, tryCons, update, remove, tryRemove) import Gargantext.Sessions.Types (Session(..), Sessions(..), OpenNodes, NodeId, mkNodeId, sessionUrl, sessionId, empty, null, unSessions, lookup, cons, tryCons, update, remove, tryRemove)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Simple.JSON as JSON import Simple.JSON as JSON
...@@ -118,7 +119,7 @@ postAuthRequest backend ar@(AuthRequest {username}) = ...@@ -118,7 +119,7 @@ postAuthRequest backend ar@(AuthRequest {username}) =
decode (Right (AuthData { token, tree_id, user_id })) = decode (Right (AuthData { token, tree_id, user_id })) =
Right $ Session { backend, caches: Map.empty, token, treeId: tree_id, username, userId: user_id } Right $ Session { backend, caches: Map.empty, token, treeId: tree_id, username, userId: user_id }
postForgotPasswordRequest :: Backend -> String -> Aff (Either String { status :: String }) postForgotPasswordRequest :: Backend -> String -> Aff (Either String GT.WorkerTask)
postForgotPasswordRequest backend email = postForgotPasswordRequest backend email =
decode <$> REST.post Nothing (toUrl backend "async/forgot-password") { email } decode <$> REST.post Nothing (toUrl backend "async/forgot-password") { email }
where where
......
...@@ -5,6 +5,7 @@ import Gargantext.Prelude ...@@ -5,6 +5,7 @@ import Gargantext.Prelude
import Data.Argonaut as Argonaut import Data.Argonaut as Argonaut
import Data.Array as A import Data.Array as A
import Data.Eq.Generic (genericEq) import Data.Eq.Generic (genericEq)
import Data.Foldable (foldl)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Int (toNumber) import Data.Int (toNumber)
import Data.Maybe (Maybe(..), maybe) import Data.Maybe (Maybe(..), maybe)
...@@ -749,64 +750,6 @@ asyncTaskTypePath UploadFile = "async/file/add/" ...@@ -749,64 +750,6 @@ asyncTaskTypePath UploadFile = "async/file/add/"
asyncTaskTypePath UploadFrameCalc = "add/framecalc/async/" asyncTaskTypePath UploadFrameCalc = "add/framecalc/async/"
type AsyncTaskID = String
data AsyncTaskStatus = IsRunning
| IsPending
| IsReceived
| IsStarted
| IsFailure
| IsFinished
| IsKilled
derive instance Generic AsyncTaskStatus _
instance JSON.ReadForeign AsyncTaskStatus where
readImpl = JSONG.enumSumRep
instance JSON.WriteForeign AsyncTaskStatus where
writeImpl = JSON.writeImpl <<< show
instance Show AsyncTaskStatus where
show = genericShow
derive instance Eq AsyncTaskStatus
-- instance Read AsyncTaskStatus where
-- read "IsFailure" = Just Failed
-- read "IsFinished" = Just Finished
-- read "IsKilled" = Just Killed
-- read "IsPending" = Just Pending
-- read "IsReceived" = Just Received
-- read "IsRunning" = Just Running
-- read "IsStarted" = Just Started
-- read _ = Nothing
newtype AsyncTask =
AsyncTask { id :: AsyncTaskID
, status :: AsyncTaskStatus
}
derive instance Generic AsyncTask _
derive instance Newtype AsyncTask _
derive newtype instance JSON.ReadForeign AsyncTask
derive newtype instance JSON.WriteForeign AsyncTask
instance Eq AsyncTask where eq = genericEq
newtype AsyncTaskWithType = AsyncTaskWithType
{ task :: AsyncTask
, typ :: AsyncTaskType
}
derive instance Generic AsyncTaskWithType _
derive instance Newtype AsyncTaskWithType _
derive newtype instance JSON.ReadForeign AsyncTaskWithType
derive newtype instance JSON.WriteForeign AsyncTaskWithType
instance Eq AsyncTaskWithType where eq = genericEq
newtype AsyncProgress = AsyncProgress
{ id :: AsyncTaskID
, error :: Maybe String
, log :: Array AsyncTaskLog
, status :: AsyncTaskStatus
}
derive instance Generic AsyncProgress _
derive instance Newtype AsyncProgress _
derive newtype instance JSON.ReadForeign AsyncProgress
newtype AsyncEvent = AsyncEvent newtype AsyncEvent = AsyncEvent
{ level :: String { level :: String
, message :: String , message :: String
...@@ -815,6 +758,10 @@ derive instance Generic AsyncEvent _ ...@@ -815,6 +758,10 @@ derive instance Generic AsyncEvent _
derive instance Newtype AsyncEvent _ derive instance Newtype AsyncEvent _
derive newtype instance JSON.ReadForeign AsyncEvent derive newtype instance JSON.ReadForeign AsyncEvent
asyncEventErrorMessage :: AsyncEvent -> Maybe String
asyncEventErrorMessage (AsyncEvent { level: "ERROR", message }) = Just message
asyncEventErrorMessage _ = Nothing
newtype AsyncTaskLog = AsyncTaskLog newtype AsyncTaskLog = AsyncTaskLog
{ events :: Array AsyncEvent { events :: Array AsyncEvent
, failed :: Int , failed :: Int
...@@ -825,15 +772,45 @@ derive instance Generic AsyncTaskLog _ ...@@ -825,15 +772,45 @@ derive instance Generic AsyncTaskLog _
derive instance Newtype AsyncTaskLog _ derive instance Newtype AsyncTaskLog _
derive newtype instance JSON.ReadForeign AsyncTaskLog derive newtype instance JSON.ReadForeign AsyncTaskLog
progressPercent :: AsyncProgress -> Number asyncTaskLogEventsErrorMessage :: AsyncTaskLog -> Maybe String
progressPercent (AsyncProgress { log }) = perc asyncTaskLogEventsErrorMessage (AsyncTaskLog { events }) =
foldl eventErrorMessage' Nothing events
where
eventErrorMessage' acc ae =
case asyncEventErrorMessage ae of
Nothing -> acc
Just e' ->
case acc of
Nothing -> Just e'
Just acc' -> Just $ e' <> "\n" <> acc'
asyncTaskLogPercent :: AsyncTaskLog -> Number
asyncTaskLogPercent (AsyncTaskLog { failed, remaining, succeeded }) = 100.0 * nom / denom
where where
perc = case A.head log of nom = toNumber $ failed + succeeded
Nothing -> 0.0 denom = toNumber $ failed + succeeded + remaining
Just (AsyncTaskLog {failed, remaining, succeeded}) -> 100.0*nom/denom
where asyncTaskLogIsFinished :: AsyncTaskLog -> Boolean
nom = toNumber $ failed + succeeded asyncTaskLogIsFinished (AsyncTaskLog { remaining }) = remaining == 0
denom = toNumber $ failed + succeeded + remaining
asyncTaskLogIsError :: AsyncTaskLog -> Boolean
asyncTaskLogIsError atl@(AsyncTaskLog { events }) =
asyncTaskLogIsFinished atl &&
(A.length $ A.filter (\(AsyncEvent { level }) -> level == "ERROR") events) > 0
-- New type tasks (async workers)
newtype WorkerTask = WorkerTask { message_id :: Number
, node_id :: Maybe NodeID }
derive instance Generic WorkerTask _
derive instance Newtype WorkerTask _
instance Eq WorkerTask where
eq = genericEq
instance Ord WorkerTask where
compare = genericCompare
instance Show WorkerTask where
show = genericShow
derive newtype instance JSON.ReadForeign WorkerTask
derive newtype instance JSON.WriteForeign WorkerTask
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
-- | GarganText Internal Sugar -- | GarganText Internal Sugar
...@@ -881,3 +858,76 @@ defaultCacheParams = CacheParams ...@@ -881,3 +858,76 @@ defaultCacheParams = CacheParams
{ expandTableEdition : false { expandTableEdition : false
, showTree : true , showTree : true
} }
--- Old-style tasks
-- type AsyncTaskID = String
-- data AsyncTaskStatus = IsRunning
-- | IsPending
-- | IsReceived
-- | IsStarted
-- | IsFailure
-- | IsFinished
-- | IsKilled
-- derive instance Generic AsyncTaskStatus _
-- instance JSON.ReadForeign AsyncTaskStatus where
-- readImpl = JSONG.enumSumRep
-- instance JSON.WriteForeign AsyncTaskStatus where
-- writeImpl = JSON.writeImpl <<< show
-- instance Show AsyncTaskStatus where
-- show = genericShow
-- derive instance Eq AsyncTaskStatus
-- instance Read AsyncTaskStatus where
-- read "IsFailure" = Just Failed
-- read "IsFinished" = Just Finished
-- read "IsKilled" = Just Killed
-- read "IsPending" = Just Pending
-- read "IsReceived" = Just Received
-- read "IsRunning" = Just Running
-- read "IsStarted" = Just Started
-- read _ = Nothing
-- newtype AsyncTask =
-- AsyncTask { id :: AsyncTaskID
-- , status :: AsyncTaskStatus
-- }
-- derive instance Generic AsyncTask _
-- derive instance Newtype AsyncTask _
-- derive newtype instance JSON.ReadForeign AsyncTask
-- derive newtype instance JSON.WriteForeign AsyncTask
-- instance Eq AsyncTask where eq = genericEq
-- newtype AsyncTaskWithType = AsyncTaskWithType
-- { task :: AsyncTask
-- , typ :: AsyncTaskType
-- }
-- derive instance Generic AsyncTaskWithType _
-- derive instance Newtype AsyncTaskWithType _
-- derive newtype instance JSON.ReadForeign AsyncTaskWithType
-- derive newtype instance JSON.WriteForeign AsyncTaskWithType
-- instance Eq AsyncTaskWithType where eq = genericEq
-- newtype AsyncProgress = AsyncProgress
-- { id :: AsyncTaskID
-- , error :: Maybe String
-- , log :: Array AsyncTaskLog
-- , status :: AsyncTaskStatus
-- }
-- derive instance Generic AsyncProgress _
-- derive instance Newtype AsyncProgress _
-- derive newtype instance JSON.ReadForeign AsyncProgress
-- progressPercent :: AsyncProgress -> Number
-- progressPercent (AsyncProgress { log }) = perc
-- where
-- perc = case A.head log of
-- Nothing -> 0.0
-- Just atl -> asyncTaskLogPercent atl
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment