Initial version.

Features:

- Upload PDF files let them analyze

- Manage meta data and items

- See processing in webapp
This commit is contained in:
Eike Kettner
2019-07-23 00:53:30 +02:00
parent 6154e6a387
commit 831cd8b655
341 changed files with 23634 additions and 484 deletions

View File

@ -0,0 +1,79 @@
module Page.Queue.Data exposing (..)
import Http
import Api.Model.JobQueueState exposing (JobQueueState)
import Api.Model.JobDetail exposing (JobDetail)
import Api.Model.BasicResult exposing (BasicResult)
import Time
import Util.Duration
import Util.Maybe
import Comp.YesNoDimmer
type alias Model =
{ state: JobQueueState
, error: String
, pollingInterval: Float
, init: Bool
, stopRefresh: Bool
, currentMillis: Int
, showLog: Maybe JobDetail
, deleteConfirm: Comp.YesNoDimmer.Model
, cancelJobRequest: Maybe String
}
emptyModel: Model
emptyModel =
{ state = Api.Model.JobQueueState.empty
, error = ""
, pollingInterval = 1200
, init = False
, stopRefresh = False
, currentMillis = 0
, showLog = Nothing
, deleteConfirm = Comp.YesNoDimmer.emptyModel
, cancelJobRequest = Nothing
}
type Msg
= Init
| StateResp (Result Http.Error JobQueueState)
| StopRefresh
| NewTime Time.Posix
| ShowLog JobDetail
| QuitShowLog
| RequestCancelJob JobDetail
| DimmerMsg JobDetail Comp.YesNoDimmer.Msg
| CancelResp (Result Http.Error BasicResult)
getRunningTime: Model -> JobDetail -> Maybe String
getRunningTime model job =
let
mkTime: Int -> Int -> Maybe String
mkTime start end =
if start < end then Just <| Util.Duration.toHuman (end - start)
else Nothing
in
case (job.started, job.finished) of
(Just sn, Just fn) ->
Util.Maybe.or
[ mkTime sn fn
, mkTime sn model.currentMillis
]
(Just sn, Nothing) ->
mkTime sn model.currentMillis
(Nothing, _) ->
Nothing
getSubmittedTime: Model -> JobDetail -> Maybe String
getSubmittedTime model job =
if model.currentMillis > job.submitted then
Just <| Util.Duration.toHuman (model.currentMillis - job.submitted)
else
Nothing
getDuration: Model -> JobDetail -> Maybe String
getDuration model job =
if job.state == "stuck" then getSubmittedTime model job
else Util.Maybe.or [ (getRunningTime model job), (getSubmittedTime model job) ]

View File

@ -0,0 +1,76 @@
module Page.Queue.Update exposing (update)
import Api
import Ports
import Page.Queue.Data exposing (..)
import Data.Flags exposing (Flags)
import Util.Http
import Time
import Task
import Comp.YesNoDimmer
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update flags msg model =
case msg of
Init ->
let
start = if model.init
then Cmd.none
else Cmd.batch
[Api.getJobQueueState flags StateResp
,getNewTime
]
in
({model|init = True, stopRefresh = False}, start)
StateResp (Ok s) ->
let
progressCmd =
List.map (\job -> Ports.setProgress (job.id, job.progress)) s.progress
_ = Debug.log "stopRefresh" model.stopRefresh
refresh =
if model.pollingInterval <= 0 || model.stopRefresh then Cmd.none
else Cmd.batch
[Api.getJobQueueStateIn flags model.pollingInterval StateResp
,getNewTime
]
in
({model | state = s, stopRefresh = False}, Cmd.batch (refresh :: progressCmd))
StateResp (Err err) ->
({model | error = Util.Http.errorToString err }, Cmd.none)
StopRefresh ->
({model | stopRefresh = True, init = False }, Cmd.none)
NewTime t ->
({model | currentMillis = Time.posixToMillis t}, Cmd.none)
ShowLog job ->
({model | showLog = Just job}, Cmd.none)
QuitShowLog ->
({model | showLog = Nothing}, Cmd.none)
RequestCancelJob job ->
let
newModel = {model|cancelJobRequest = Just job.id}
in
update flags (DimmerMsg job Comp.YesNoDimmer.Activate) newModel
DimmerMsg job m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
cmd = if confirmed then Api.cancelJob flags job.id CancelResp else Cmd.none
in
({model | deleteConfirm = cm}, cmd)
CancelResp (Ok r) ->
(model, Cmd.none)
CancelResp (Err err) ->
(model, Cmd.none)
getNewTime : Cmd Msg
getNewTime =
Task.perform NewTime Time.now

View File

@ -0,0 +1,217 @@
module Page.Queue.View exposing (view)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Page.Queue.Data exposing (..)
import Api.Model.JobQueueState exposing (JobQueueState)
import Api.Model.JobDetail exposing (JobDetail)
import Api.Model.JobLogEvent exposing (JobLogEvent)
import Data.Priority
import Comp.YesNoDimmer
import Util.Time exposing (formatDateTime, formatIsoDateTime)
import Util.Duration
view: Model -> Html Msg
view model =
div [class "queue-page ui grid container"] <|
List.concat
[ case model.showLog of
Just job ->
[renderJobLog job]
Nothing ->
List.map (renderProgressCard model) model.state.progress
|> List.map (\el -> div [class "row"][div [class "column"][el]])
, [div [class "two column row"]
[renderWaiting model
,renderCompleted model
]
]
]
renderJobLog: JobDetail -> Html Msg
renderJobLog job =
div [class "ui fluid card"]
[div [class "content"]
[i [class "delete link icon", onClick QuitShowLog][]
,text job.name
]
,div [class "content"]
[div [class "job-log"]
(List.map renderLogLine job.logs)
]
]
renderWaiting: Model -> Html Msg
renderWaiting model =
div [class "column"]
[div [class "ui center aligned basic segment"]
[i [class "ui large angle double up icon"][]
]
,div [class "ui centered cards"]
(List.map (renderInfoCard model) model.state.queued)
]
renderCompleted: Model -> Html Msg
renderCompleted model =
div [class "column"]
[div [class "ui center aligned basic segment"]
[i [class "ui large angle double down icon"][]
]
,div [class "ui centered cards"]
(List.map (renderInfoCard model) model.state.completed)
]
renderProgressCard: Model -> JobDetail -> Html Msg
renderProgressCard model job =
div [class "ui fluid card"]
[div [id job.id, class "ui top attached indicating progress"]
[div [class "bar"]
[]
]
,Html.map (DimmerMsg job) (Comp.YesNoDimmer.view2 (model.cancelJobRequest == Just job.id) dimmerSettings model.deleteConfirm)
,div [class "content"]
[ div [class "right floated meta"]
[div [class "ui label"]
[text job.state
,div [class "detail"]
[Maybe.withDefault "" job.worker |> text
]
]
,div [class "ui basic label"]
[i [class "clock icon"][]
,div [class "detail"]
[getDuration model job |> Maybe.withDefault "-:-" |> text
]
]
]
, i [class "asterisk loading icon"][]
, text job.name
]
,div [class "content"]
[div [class "job-log"]
(List.map renderLogLine job.logs)
]
,div [class "meta"]
[div [class "right floated"]
[button [class "ui button", onClick (RequestCancelJob job)]
[text "Cancel"
]
]
]
]
renderLogLine: JobLogEvent -> Html Msg
renderLogLine log =
span [class (String.toLower log.level)]
[formatIsoDateTime log.time |> text
,text ": "
,text log.message
, br[][]
]
isFinal: JobDetail -> Bool
isFinal job =
case job.state of
"failed" -> True
"success" -> True
"cancelled" -> True
_ -> False
dimmerSettings: Comp.YesNoDimmer.Settings
dimmerSettings =
let
defaults = Comp.YesNoDimmer.defaultSettings
in
{ defaults | headerClass = "ui inverted header", headerIcon = "", message = "Cancel/Delete this job?"}
renderInfoCard: Model -> JobDetail -> Html Msg
renderInfoCard model job =
div [classList [("ui fluid card", True)
,(jobStateColor job, True)
]
]
[Html.map (DimmerMsg job) (Comp.YesNoDimmer.view2 (model.cancelJobRequest == Just job.id) dimmerSettings model.deleteConfirm)
,div [class "content"]
[div [class "right floated"]
[if isFinal job || job.state == "stuck" then
span [onClick (ShowLog job)]
[i [class "file link icon", title "Show log"][]
]
else
span[][]
,i [class "delete link icon", title "Remove", onClick (RequestCancelJob job)][]
]
,if isFinal job then
span [class "invisible"][]
else
div [class "right floated"]
[div [class "meta"]
[getDuration model job |> Maybe.withDefault "-:-" |> text
]
]
,i [classList [("check icon", job.state == "success")
,("redo icon", job.state == "stuck")
,("bolt icon", job.state == "failed")
,("meh outline icon", job.state == "canceled")
,("cog icon", not (isFinal job) && job.state /= "stuck")
]
][]
,text job.name
]
,div [class "content"]
[div [class "right floated"]
[if isFinal job then
div [class ("ui basic label " ++ jobStateColor job)]
[i [class "clock icon"][]
,div [class "detail"]
[getDuration model job |> Maybe.withDefault "-:-" |> text
]
]
else
span [class "invisible"][]
,div [class ("ui basic label " ++ jobStateColor job)]
[text "Prio"
,div [class "detail"]
[code [][Data.Priority.fromString job.priority
|> Maybe.map Data.Priority.toName
|> Maybe.withDefault job.priority
|> text
]
]
]
,div [class ("ui basic label " ++ jobStateColor job)]
[text "Retries"
,div [class "detail"]
[job.retries |> String.fromInt |> text
]
]
]
,jobStateLabel job
,div [class "ui basic label"]
[Util.Time.formatDateTime job.submitted |> text
]
]
]
jobStateColor: JobDetail -> String
jobStateColor job =
case job.state of
"success" -> "green"
"failed" -> "red"
"canceled" -> "orange"
"stuck" -> "purple"
"scheduled" -> "blue"
"waiting" -> "grey"
_ -> ""
jobStateLabel: JobDetail -> Html Msg
jobStateLabel job =
let
col = jobStateColor job
in
div [class ("ui label " ++ col)]
[text job.state
]