From 06879456a6b3bb25bc758fc6734b14191ef91c96 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sat, 5 Sep 2020 16:00:41 +0200 Subject: [PATCH] Change job priority on queue page --- .../scala/docspell/backend/ops/OJob.scala | 7 +++ .../main/scala/docspell/common/JobState.scala | 2 + .../src/main/resources/docspell-openapi.yml | 42 ++++++++++++++++ .../restserver/routes/JobQueueRoutes.scala | 9 ++++ .../scala/docspell/store/records/RJob.scala | 7 +++ modules/webapp/src/main/elm/Api.elm | 18 +++++++ modules/webapp/src/main/elm/Data/Priority.elm | 11 ++++ .../webapp/src/main/elm/Page/Queue/Data.elm | 2 + .../webapp/src/main/elm/Page/Queue/Update.elm | 3 ++ .../webapp/src/main/elm/Page/Queue/View.elm | 50 +++++++++++++++---- 10 files changed, 140 insertions(+), 11 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala index ade2fda0..4b83a30a 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala @@ -5,8 +5,10 @@ import cats.effect._ import cats.implicits._ import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult} +import docspell.common.Priority import docspell.common.{Ident, JobState} import docspell.store.Store +import docspell.store.UpdateResult import docspell.store.queries.QJob import docspell.store.records.{RJob, RJobLog} @@ -15,6 +17,8 @@ trait OJob[F[_]] { def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] + + def setPriority(id: Ident, collective: Ident, prio: Priority): F[UpdateResult] } object OJob { @@ -56,6 +60,9 @@ object OJob { .toVector .map(CollectiveQueueState) + def setPriority(id: Ident, collective: Ident, prio: Priority): F[UpdateResult] = + UpdateResult.fromUpdate(store.transact(RJob.setPriority(id, collective, prio))) + def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = { def remove(job: RJob): F[JobCancelResult] = store.transact(RJob.delete(job.id)) *> JobCancelResult.removed.pure[F] diff --git a/modules/common/src/main/scala/docspell/common/JobState.scala b/modules/common/src/main/scala/docspell/common/JobState.scala index 007bd478..69f0e622 100644 --- a/modules/common/src/main/scala/docspell/common/JobState.scala +++ b/modules/common/src/main/scala/docspell/common/JobState.scala @@ -12,6 +12,8 @@ object JobState { /** Waiting for being executed. */ case object Waiting extends JobState {} + def waiting: JobState = Waiting + /** A scheduler has picked up this job and will pass it to the next * free slot. */ diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index a03a0e2e..bb90f3bc 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -2167,6 +2167,30 @@ paths: application/json: schema: $ref: "#/components/schemas/BasicResult" + /sec/queue/{id}/priority: + post: + tags: [ Job Queue ] + summary: Change the priority of a waiting job. + description: | + A waiting job can change its priority. If the job is not in + state waiting, this operation fails. + security: + - authTokenHeader: [] + parameters: + - $ref: "#/components/parameters/id" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/JobPriority" + responses: + 200: + description: Ok + content: + application/json: + schema: + $ref: "#/components/schemas/BasicResult" + /sec/email/settings/smtp: get: tags: [ E-Mail ] @@ -2678,6 +2702,18 @@ paths: components: schemas: + JobPriority: + description: | + Transfer the priority of a job. + required: + - priority + properties: + priority: + type: string + format: priority + enum: + - high + - low IdList: description: A list of identifiers. @@ -3512,6 +3548,9 @@ components: priority: type: string format: priority + enum: + - high + - low state: type: string format: jobstate @@ -3744,6 +3783,9 @@ components: priority: type: string format: priority + enum: + - high + - low folder: type: string format: ident diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala index 4a34b219..751da6b6 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala @@ -6,9 +6,11 @@ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident +import docspell.restapi.model.JobPriority import docspell.restserver.conv.Conversions import org.http4s.HttpRoutes +import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl @@ -31,6 +33,13 @@ object JobQueueRoutes { result <- backend.job.cancelJob(id, user.account.collective) resp <- Ok(Conversions.basicResult(result)) } yield resp + + case req @ POST -> Root / Ident(id) / "priority" => + for { + prio <- req.as[JobPriority] + res <- backend.job.setPriority(id, user.account.collective, prio.priority) + resp <- Ok(Conversions.basicResult(res, "Job priority changed")) + } yield resp } } diff --git a/modules/store/src/main/scala/docspell/store/records/RJob.scala b/modules/store/src/main/scala/docspell/store/records/RJob.scala index 089a6866..0bc2dc14 100644 --- a/modules/store/src/main/scala/docspell/store/records/RJob.scala +++ b/modules/store/src/main/scala/docspell/store/records/RJob.scala @@ -231,6 +231,13 @@ object RJob { ) ).update.run + def setPriority(jobId: Ident, jobGroup: Ident, prio: Priority): ConnectionIO[Int] = + updateRow( + table, + and(id.is(jobId), group.is(jobGroup), state.is(JobState.waiting)), + priority.setTo(prio) + ).update.run + def getRetries(jobId: Ident): ConnectionIO[Option[Int]] = selectSimple(List(retries), table, id.is(jobId)).query[Int].option diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index ccba8570..c90865ab 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -86,6 +86,7 @@ module Api exposing , setItemDueDate , setItemName , setItemNotes + , setJobPrio , setTags , setUnconfirmed , startClassifier @@ -129,6 +130,7 @@ import Api.Model.ItemLightList exposing (ItemLightList) import Api.Model.ItemProposals exposing (ItemProposals) import Api.Model.ItemSearch exposing (ItemSearch) import Api.Model.ItemUploadMeta exposing (ItemUploadMeta) +import Api.Model.JobPriority exposing (JobPriority) import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.MoveAttachment exposing (MoveAttachment) import Api.Model.NewFolder exposing (NewFolder) @@ -160,6 +162,7 @@ import Api.Model.UserPass exposing (UserPass) import Api.Model.VersionInfo exposing (VersionInfo) import Data.ContactType exposing (ContactType) import Data.Flags exposing (Flags) +import Data.Priority exposing (Priority) import File exposing (File) import Http import Json.Encode as JsonEncode @@ -1197,6 +1200,21 @@ deleteUser flags user receive = --- Job Queue +setJobPrio : Flags -> String -> Priority -> (Result Http.Error BasicResult -> msg) -> Cmd msg +setJobPrio flags jobid prio receive = + Http2.authPost + { url = flags.config.baseUrl ++ "/api/v1/sec/queue/" ++ jobid ++ "/priority" + , account = getAccount flags + , body = + Data.Priority.toName prio + |> String.toLower + |> JobPriority + |> Api.Model.JobPriority.encode + |> Http.jsonBody + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + cancelJob : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg cancelJob flags jobid receive = Http2.authPost diff --git a/modules/webapp/src/main/elm/Data/Priority.elm b/modules/webapp/src/main/elm/Data/Priority.elm index 2fefd347..3ba7828d 100644 --- a/modules/webapp/src/main/elm/Data/Priority.elm +++ b/modules/webapp/src/main/elm/Data/Priority.elm @@ -2,6 +2,7 @@ module Data.Priority exposing ( Priority(..) , all , fromString + , next , toName ) @@ -38,6 +39,16 @@ toName lang = "High" +next : Priority -> Priority +next prio = + case prio of + High -> + Low + + Low -> + High + + all : List Priority all = [ Low, High ] diff --git a/modules/webapp/src/main/elm/Page/Queue/Data.elm b/modules/webapp/src/main/elm/Page/Queue/Data.elm index be6a5b5b..9ec44c8f 100644 --- a/modules/webapp/src/main/elm/Page/Queue/Data.elm +++ b/modules/webapp/src/main/elm/Page/Queue/Data.elm @@ -10,6 +10,7 @@ import Api.Model.BasicResult exposing (BasicResult) import Api.Model.JobDetail exposing (JobDetail) import Api.Model.JobQueueState exposing (JobQueueState) import Comp.YesNoDimmer +import Data.Priority exposing (Priority) import Http import Time import Util.Duration @@ -53,6 +54,7 @@ type Msg | RequestCancelJob JobDetail | DimmerMsg JobDetail Comp.YesNoDimmer.Msg | CancelResp (Result Http.Error BasicResult) + | ChangePrio String Priority getRunningTime : Model -> JobDetail -> Maybe String diff --git a/modules/webapp/src/main/elm/Page/Queue/Update.elm b/modules/webapp/src/main/elm/Page/Queue/Update.elm index 31a5d6e4..d26201f2 100644 --- a/modules/webapp/src/main/elm/Page/Queue/Update.elm +++ b/modules/webapp/src/main/elm/Page/Queue/Update.elm @@ -86,6 +86,9 @@ update flags msg model = CancelResp (Err _) -> ( model, Cmd.none ) + ChangePrio id prio -> + ( model, Api.setJobPrio flags id prio CancelResp ) + getNewTime : Cmd Msg getNewTime = diff --git a/modules/webapp/src/main/elm/Page/Queue/View.elm b/modules/webapp/src/main/elm/Page/Queue/View.elm index c0285e29..7fc2a67c 100644 --- a/modules/webapp/src/main/elm/Page/Queue/View.elm +++ b/modules/webapp/src/main/elm/Page/Queue/View.elm @@ -143,6 +143,11 @@ dimmerSettings = renderInfoCard : Model -> JobDetail -> Html Msg renderInfoCard model job = + let + prio = + Data.Priority.fromString job.priority + |> Maybe.withDefault Data.Priority.Low + in div [ classList [ ( "ui fluid card", True ) @@ -194,23 +199,46 @@ renderInfoCard model job = 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 ] ] + , case job.state of + "waiting" -> + a + [ class ("ui basic label " ++ jobStateColor job) + , onClick (ChangePrio job.id (Data.Priority.next prio)) + , href "#" + , title "Change priority of this job" + ] + [ i [ class "sort numeric up icon" ] [] + , 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 "Prio" + , div [ class "detail" ] + [ code [] + [ Data.Priority.fromString job.priority + |> Maybe.map Data.Priority.toName + |> Maybe.withDefault job.priority + |> text + ] + ] + ] ] , jobStateLabel job , div [ class "ui basic label" ]