Add start-now button for train-classifier task

This commit is contained in:
Eike Kettner 2020-09-01 23:56:57 +02:00
parent 8677eca6d4
commit f9fcee81a5
6 changed files with 115 additions and 15 deletions

View File

@ -53,7 +53,7 @@ object BackendApp {
loginImpl <- Login[F](store) loginImpl <- Login[F](store)
signupImpl <- OSignup[F](store) signupImpl <- OSignup[F](store)
joexImpl <- OJoex(JoexClient(httpClient), store) joexImpl <- OJoex(JoexClient(httpClient), store)
collImpl <- OCollective[F](store, utStore, joexImpl) collImpl <- OCollective[F](store, utStore, queue, joexImpl)
sourceImpl <- OSource[F](store) sourceImpl <- OSource[F](store)
tagImpl <- OTag[F](store) tagImpl <- OTag[F](store)
equipImpl <- OEquipment[F](store) equipImpl <- OEquipment[F](store)

View File

@ -8,12 +8,13 @@ import docspell.backend.PasswordCrypt
import docspell.backend.ops.OCollective._ import docspell.backend.ops.OCollective._
import docspell.common._ import docspell.common._
import docspell.store.queries.QCollective import docspell.store.queries.QCollective
import docspell.store.queue.JobQueue
import docspell.store.records._ import docspell.store.records._
import docspell.store.usertask.UserTask import docspell.store.usertask.UserTask
import docspell.store.usertask.UserTaskStore import docspell.store.usertask.UserTaskStore
import docspell.store.{AddResult, Store} import docspell.store.{AddResult, Store}
import com.github.eikek.calev.CalEvent import com.github.eikek.calev._
trait OCollective[F[_]] { trait OCollective[F[_]] {
@ -49,6 +50,7 @@ trait OCollective[F[_]] {
def findEnabledSource(sourceId: Ident): F[Option[RSource]] def findEnabledSource(sourceId: Ident): F[Option[RSource]]
def startLearnClassifier(collective: Ident): F[Unit]
} }
object OCollective { object OCollective {
@ -102,6 +104,7 @@ object OCollective {
def apply[F[_]: Effect]( def apply[F[_]: Effect](
store: Store[F], store: Store[F],
uts: UserTaskStore[F], uts: UserTaskStore[F],
queue: JobQueue[F],
joex: OJoex[F] joex: OJoex[F]
): Resource[F, OCollective[F]] = ): Resource[F, OCollective[F]] =
Resource.pure[F, OCollective[F]](new OCollective[F] { Resource.pure[F, OCollective[F]](new OCollective[F] {
@ -131,6 +134,21 @@ object OCollective {
_ <- joex.notifyAllNodes _ <- joex.notifyAllNodes
} yield () } yield ()
def startLearnClassifier(collective: Ident): F[Unit] =
for {
id <- Ident.randomId[F]
ut <- UserTask(
id,
LearnClassifierArgs.taskName,
true,
CalEvent(WeekdayComponent.All, DateEvent.All, TimeEvent.All),
LearnClassifierArgs(collective)
).encode.toPeriodicTask(AccountId(collective, LearnClassifierArgs.taskName))
job <- ut.toJob
_ <- queue.insert(job)
_ <- joex.notifyAllNodes
} yield ()
def findSettings(collective: Ident): F[Option[OCollective.Settings]] = def findSettings(collective: Ident): F[Option[OCollective.Settings]] =
store.transact(RCollective.getSettings(collective)) store.transact(RCollective.getSettings(collective))

View File

@ -1047,6 +1047,28 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/ContactList" $ref: "#/components/schemas/ContactList"
/sec/collective/classifier/startonce:
post:
tags: [ Collective ]
summary: Starts the learn-classifier task
description: |
If the collective has classification enabled, this will submit
the task for learning a classifier from existing data. This
task is usally run periodically as determined by the
collective settings.
The request is empty, settings are used from the collective.
security:
- authTokenHeader: []
responses:
200:
description: Ok
content:
application/json:
schema:
$ref: "#/components/schemas/BasicResult"
/sec/user: /sec/user:
get: get:
tags: [ Collective ] tags: [ Collective ]

View File

@ -88,6 +88,12 @@ object CollectiveRoutes {
resp <- Ok(ContactList(res.map(Conversions.mkContact))) resp <- Ok(ContactList(res.map(Conversions.mkContact)))
} yield resp } yield resp
case POST -> Root / "classifier" / "startonce" =>
for {
_ <- backend.collective.startLearnClassifier(user.account.collective)
resp <- Ok(BasicResult(true, "Task submitted"))
} yield resp
case GET -> Root => case GET -> Root =>
for { for {
collDb <- backend.collective.find(user.account.collective) collDb <- backend.collective.find(user.account.collective)

View File

@ -88,6 +88,7 @@ module Api exposing
, setItemNotes , setItemNotes
, setTags , setTags
, setUnconfirmed , setUnconfirmed
, startClassifier
, startOnceNotifyDueItems , startOnceNotifyDueItems
, startOnceScanMailbox , startOnceScanMailbox
, startReIndex , startReIndex
@ -795,6 +796,19 @@ versionInfo flags receive =
--- Collective --- Collective
startClassifier :
Flags
-> (Result Http.Error BasicResult -> msg)
-> Cmd msg
startClassifier flags receive =
Http2.authPost
{ url = flags.config.baseUrl ++ "/api/v1/sec/collective/classifier/startonce"
, account = getAccount flags
, body = Http.emptyBody
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
}
getTagCloud : Flags -> (Result Http.Error TagCloud -> msg) -> Cmd msg getTagCloud : Flags -> (Result Http.Error TagCloud -> msg) -> Cmd msg
getTagCloud flags receive = getTagCloud flags receive =
Http2.authGet Http2.authGet

View File

@ -30,6 +30,7 @@ type alias Model =
, fullTextConfirmText : String , fullTextConfirmText : String
, fullTextReIndexResult : Maybe BasicResult , fullTextReIndexResult : Maybe BasicResult
, classifierModel : Comp.ClassifierSettingsForm.Model , classifierModel : Comp.ClassifierSettingsForm.Model
, startClassifierResult : Maybe BasicResult
} }
@ -60,6 +61,7 @@ init flags settings =
, fullTextConfirmText = "" , fullTextConfirmText = ""
, fullTextReIndexResult = Nothing , fullTextReIndexResult = Nothing
, classifierModel = cm , classifierModel = cm
, startClassifierResult = Nothing
} }
, Cmd.map ClassifierSettingMsg cc , Cmd.map ClassifierSettingMsg cc
) )
@ -91,6 +93,8 @@ type Msg
| TriggerReIndexResult (Result Http.Error BasicResult) | TriggerReIndexResult (Result Http.Error BasicResult)
| ClassifierSettingMsg Comp.ClassifierSettingsForm.Msg | ClassifierSettingMsg Comp.ClassifierSettingsForm.Msg
| SaveSettings | SaveSettings
| StartClassifierTask
| StartClassifierResp (Result Http.Error BasicResult)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe CollectiveSettings ) update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe CollectiveSettings )
@ -169,12 +173,30 @@ update flags msg model =
_ -> _ ->
( model, Cmd.none, Nothing ) ( model, Cmd.none, Nothing )
StartClassifierTask ->
( model, Api.startClassifier flags StartClassifierResp, Nothing )
StartClassifierResp (Ok br) ->
( { model | startClassifierResult = Just br }
, Cmd.none
, Nothing
)
StartClassifierResp (Err err) ->
( { model
| startClassifierResult =
Just (BasicResult False (Util.Http.errorToString err))
}
, Cmd.none
, Nothing
)
view : Flags -> UiSettings -> Model -> Html Msg view : Flags -> UiSettings -> Model -> Html Msg
view flags settings model = view flags settings model =
div div
[ classList [ classList
[ ( "ui form", True ) [ ( "ui form error success", True )
, ( "error", Maybe.map .success model.fullTextReIndexResult == Just False ) , ( "error", Maybe.map .success model.fullTextReIndexResult == Just False )
, ( "success", Maybe.map .success model.fullTextReIndexResult == Just True ) , ( "success", Maybe.map .success model.fullTextReIndexResult == Just True )
] ]
@ -250,18 +272,7 @@ view flags settings model =
[ text "This starts a task that clears the full-text index and re-indexes all your data again." [ text "This starts a task that clears the full-text index and re-indexes all your data again."
, text "You must type OK before clicking the button to avoid accidental re-indexing." , text "You must type OK before clicking the button to avoid accidental re-indexing."
] ]
, div , renderResultMessage model.fullTextReIndexResult
[ classList
[ ( "ui message", True )
, ( "error", Maybe.map .success model.fullTextReIndexResult == Just False )
, ( "success", Maybe.map .success model.fullTextReIndexResult == Just True )
, ( "hidden invisible", model.fullTextReIndexResult == Nothing )
]
]
[ Maybe.map .message model.fullTextReIndexResult
|> Maybe.withDefault ""
|> text
]
] ]
, h3 , h3
[ classList [ classList
@ -279,6 +290,19 @@ view flags settings model =
] ]
[ Html.map ClassifierSettingMsg [ Html.map ClassifierSettingMsg
(Comp.ClassifierSettingsForm.view model.classifierModel) (Comp.ClassifierSettingsForm.view model.classifierModel)
, div [ class "ui vertical segment" ]
[ button
[ classList
[ ( "ui small secondary basic button", True )
, ( "disabled", not model.classifierModel.enabled )
]
, title "Starts a task to train a classifier"
, onClick StartClassifierTask
]
[ text "Start now"
]
, renderResultMessage model.startClassifierResult
]
] ]
, div [ class "ui divider" ] [] , div [ class "ui divider" ] []
, button , button
@ -291,3 +315,19 @@ view flags settings model =
[ text "Save" [ text "Save"
] ]
] ]
renderResultMessage : Maybe BasicResult -> Html msg
renderResultMessage result =
div
[ classList
[ ( "ui message", True )
, ( "error", Maybe.map .success result == Just False )
, ( "success", Maybe.map .success result == Just True )
, ( "hidden invisible", result == Nothing )
]
]
[ Maybe.map .message result
|> Maybe.withDefault ""
|> text
]