From d2edddd23896c6428576e8a29ceeff82c178dc73 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sat, 8 Feb 2020 12:23:59 +0100 Subject: [PATCH] Show attachment meta data in ui Allow to view the extracted text and results from text analysis of an attachment. --- modules/webapp/src/main/elm/Api.elm | 19 ++ .../src/main/elm/Comp/AttachmentMeta.elm | 209 ++++++++++++++++++ .../webapp/src/main/elm/Comp/ItemDetail.elm | 80 ++++++- modules/webapp/src/main/webjar/docspell.css | 9 + 4 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 modules/webapp/src/main/elm/Comp/AttachmentMeta.elm diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index 12098ea2..dbc2f473 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -10,6 +10,7 @@ module Api exposing , deleteSource , deleteTag , deleteUser + , getAttachmentMeta , getCollective , getCollectiveSettings , getContacts @@ -61,6 +62,7 @@ module Api exposing , versionInfo ) +import Api.Model.AttachmentMeta exposing (AttachmentMeta) import Api.Model.AuthResult exposing (AuthResult) import Api.Model.BasicResult exposing (BasicResult) import Api.Model.Collective exposing (Collective) @@ -112,6 +114,23 @@ import Util.Http as Http2 +--- Attachment Metadata + + +getAttachmentMeta : + Flags + -> String + -> (Result Http.Error AttachmentMeta -> msg) + -> Cmd msg +getAttachmentMeta flags id receive = + Http2.authGet + { url = flags.config.baseUrl ++ "/api/v1/sec/attachment/" ++ id ++ "/meta" + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.AttachmentMeta.decoder + } + + + --- Get Sent Mails diff --git a/modules/webapp/src/main/elm/Comp/AttachmentMeta.elm b/modules/webapp/src/main/elm/Comp/AttachmentMeta.elm new file mode 100644 index 00000000..bfd79607 --- /dev/null +++ b/modules/webapp/src/main/elm/Comp/AttachmentMeta.elm @@ -0,0 +1,209 @@ +module Comp.AttachmentMeta exposing + ( Model + , Msg + , init + , update + , view + ) + +import Api +import Api.Model.AttachmentMeta exposing (AttachmentMeta) +import Api.Model.ItemProposals exposing (ItemProposals) +import Api.Model.Label exposing (Label) +import Data.Flags exposing (Flags) +import Html exposing (..) +import Html.Attributes exposing (..) +import Http +import Util.Http +import Util.Time + + +type alias Model = + { id : String + , meta : DataResult AttachmentMeta + } + + +type DataResult a + = NotAvailable + | Success a + | Failure String + + +emptyModel : Model +emptyModel = + { id = "" + , meta = NotAvailable + } + + +init : Flags -> String -> ( Model, Cmd Msg ) +init flags id = + ( { emptyModel | id = id } + , Api.getAttachmentMeta flags id MetaResp + ) + + +type Msg + = MetaResp (Result Http.Error AttachmentMeta) + + +update : Msg -> Model -> Model +update msg model = + case msg of + MetaResp (Ok am) -> + { model | meta = Success am } + + MetaResp (Err err) -> + { model | meta = Failure (Util.Http.errorToString err) } + + +view : Model -> Html Msg +view model = + div [] + [ h3 [ class "ui header" ] + [ text "Extracted Meta Data" + ] + , case model.meta of + NotAvailable -> + div [ class "ui active dimmer" ] + [ div [ class "ui loader" ] + [] + ] + + Failure msg -> + div [ class "ui error message" ] + [ text msg + ] + + Success data -> + viewData data + ] + + +viewData : AttachmentMeta -> Html Msg +viewData meta = + div [] + [ div [ class "ui dividing header" ] + [ text "Content" + ] + , div [ class "extracted-text" ] + [ text meta.content + ] + , div [ class "ui dividing header" ] + [ text "Labels" + ] + , div [] + [ div [ class "ui horizontal list" ] + (List.map renderLabelItem meta.labels) + ] + , div [ class "ui dividing header" ] + [ text "Proposals" + ] + , viewProposals meta.proposals + ] + + +viewProposals : ItemProposals -> Html Msg +viewProposals props = + let + mkItem n lbl = + div [ class "item" ] + [ div [ class "ui label" ] + [ text lbl.name + , div [ class "detail" ] + [ (String.fromInt (n + 1) ++ ".") + |> text + ] + ] + ] + + mkTimeItem ms = + div [ class "item" ] + [ div [ class "ui label" ] + [ Util.Time.formatDateShort ms |> text + ] + ] + in + div [] + [ div [ class "ui small header" ] + [ text "Correspondent Organization" + ] + , div [ class "ui horizontal list" ] + (List.indexedMap mkItem props.corrOrg) + , div [ class "ui small header" ] + [ text "Correspondent Person" + ] + , div [ class "ui horizontal list" ] + (List.indexedMap mkItem props.corrPerson) + , div [ class "ui small header" ] + [ text "Concerning Person" + ] + , div [ class "ui horizontal list" ] + (List.indexedMap mkItem props.concPerson) + , div [ class "ui small header" ] + [ text "Concerning Equipment" + ] + , div [ class "ui horizontal list" ] + (List.indexedMap mkItem props.concEquipment) + , div [ class "ui small header" ] + [ text "Item Date" + ] + , div [ class "ui horizontal list" ] + (List.map mkTimeItem props.itemDate) + , div [ class "ui small header" ] + [ text "Item Due Date" + ] + , div [ class "ui horizontal list" ] + (List.map mkTimeItem props.dueDate) + ] + + +renderLabelItem : Label -> Html Msg +renderLabelItem label = + div [ class "item" ] + [ renderLabel label + ] + + +renderLabel : Label -> Html Msg +renderLabel label = + let + icon = + case label.labelType of + "organization" -> + "factory icon" + + "person" -> + "user icon" + + "location" -> + "map marker icon" + + "date" -> + "calendar alternate icon" + + "misc" -> + "help icon" + + "email" -> + "at icon" + + "website" -> + "external alternate icon" + + _ -> + "tag icon" + in + div + [ class "ui basic label" + , title label.labelType + ] + [ i [ class icon ] [] + , text label.label + , div [ class "detail" ] + [ String.fromInt label.beginPos |> text + , text "-" + , String.fromInt label.endPos |> text + ] + ] diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail.elm b/modules/webapp/src/main/elm/Comp/ItemDetail.elm index 2d71e324..e95dcd84 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail.elm @@ -22,6 +22,7 @@ import Api.Model.SentMails exposing (SentMails) import Api.Model.Tag exposing (Tag) import Api.Model.TagList exposing (TagList) import Browser.Navigation as Nav +import Comp.AttachmentMeta import Comp.DatePicker import Comp.Dropdown exposing (isDropdownChangeMsg) import Comp.ItemMail @@ -31,6 +32,7 @@ import Comp.YesNoDimmer import Data.Direction exposing (Direction) import Data.Flags exposing (Flags) import DatePicker exposing (DatePicker) +import Dict exposing (Dict) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick, onInput) @@ -69,6 +71,8 @@ type alias Model = , mailSendResult : Maybe BasicResult , sentMails : Comp.SentMails.Model , sentMailsOpen : Bool + , attachMeta : Dict String Comp.AttachmentMeta.Model + , attachMetaOpen : Bool } @@ -153,6 +157,8 @@ emptyModel = , mailSendResult = Nothing , sentMails = Comp.SentMails.init , sentMailsOpen = False + , attachMeta = Dict.empty + , attachMetaOpen = False } @@ -203,6 +209,8 @@ type Msg | SentMailsMsg Comp.SentMails.Msg | ToggleSentMails | SentMailsResp (Result Http.Error SentMails) + | AttachMetaClick String + | AttachMetaMsg String Comp.AttachmentMeta.Msg @@ -877,6 +885,39 @@ update key flags next msg model = SentMailsResp (Err _) -> ( model, Cmd.none ) + AttachMetaClick id -> + case Dict.get id model.attachMeta of + Just _ -> + ( { model | attachMetaOpen = not model.attachMetaOpen } + , Cmd.none + ) + + Nothing -> + let + ( am, ac ) = + Comp.AttachmentMeta.init flags id + + nextMeta = + Dict.insert id am model.attachMeta + in + ( { model | attachMeta = nextMeta, attachMetaOpen = True } + , Cmd.map (AttachMetaMsg id) ac + ) + + AttachMetaMsg id lmsg -> + case Dict.get id model.attachMeta of + Just cm -> + let + am = + Comp.AttachmentMeta.update lmsg cm + in + ( { model | attachMeta = Dict.insert id am model.attachMeta } + , Cmd.none + ) + + Nothing -> + ( model, Cmd.none ) + -- view @@ -1007,7 +1048,7 @@ renderNotes model = Nothing -> [] - Just str -> + Just _ -> [ div [ class "ui segment" ] [ a [ class "ui top left attached label" @@ -1135,6 +1176,17 @@ renderAttachmentView model pos attach = ] , div [ class "right menu" ] [ a + [ classList + [ ( "toggle item", True ) + , ( "active", isAttachMetaOpen model attach.id ) + ] + , title "Show extracted data" + , onClick (AttachMetaClick attach.id) + , href "#" + ] + [ i [ class "info icon" ] [] + ] + , a [ class "item" , title "Download to disk" , download attachName @@ -1144,13 +1196,37 @@ renderAttachmentView model pos attach = ] ] ] - , div [ class "ui 4:3 embed doc-embed" ] + , div + [ classList + [ ( "ui 4:3 embed doc-embed", True ) + , ( "invisible hidden", isAttachMetaOpen model attach.id ) + ] + ] [ embed [ src fileUrl, type_ attach.contentType ] [] ] + , div + [ classList + [ ( "ui basic segment", True ) + , ( "invisible hidden", not (isAttachMetaOpen model attach.id) ) + ] + ] + [ case Dict.get attach.id model.attachMeta of + Just am -> + Html.map (AttachMetaMsg attach.id) + (Comp.AttachmentMeta.view am) + + Nothing -> + span [] [] + ] ] +isAttachMetaOpen : Model -> String -> Bool +isAttachMetaOpen model id = + model.attachMetaOpen && (Dict.get id model.attachMeta /= Nothing) + + renderAttachmentsTabBody : Model -> List (Html Msg) renderAttachmentsTabBody model = let diff --git a/modules/webapp/src/main/webjar/docspell.css b/modules/webapp/src/main/webjar/docspell.css index aa60a2e6..56191e65 100644 --- a/modules/webapp/src/main/webjar/docspell.css +++ b/modules/webapp/src/main/webjar/docspell.css @@ -39,6 +39,15 @@ padding: 0 1em; } +.default-layout .extracted-text { + font-family: monospace; + white-space: pre-wrap; + max-height: 20rem; + overflow: scroll; + background: floralwhite; + padding: 0.8em; +} + .markdown-preview { overflow: auto; max-height: 300px;