mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 15:15:58 +00:00
Add start-now button for train-classifier task
This commit is contained in:
parent
8677eca6d4
commit
f9fcee81a5
@ -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)
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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 ]
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user