Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add OCR button in UI #566

Merged
merged 2 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/standard/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ RUN apk update && apk add linux-headers python3-dev \

RUN pip install --upgrade poetry roco==0.4.2
RUN curl -L -o /bin/env2js /~https://github.com/papermerge/env2js/releases/download/0.2/env2js.amd64
RUN chmod +x /bin/env2js

COPY poetry.lock pyproject.toml README.md LICENSE ${CORE_APP}/

Expand Down
4 changes: 4 additions & 0 deletions docker/standard/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,17 @@ case $CMD in
exec_init
# TODO: replace roco with env2js
roco > /usr/share/nginx/html/auth_server/papermerge-runtime-config.js
# Once user options endpoint is implemented, following two lines will removed
/bin/env2js -f /core_app/core.js.tmpl > /usr/share/nginx/html/ui/papermerge-runtime-config.js
sed -i '/Papermerge/a <script type="module" src="/papermerge-runtime-config.js"></script>' /usr/share/nginx/html/ui/index.html
exec /usr/bin/supervisord -c /etc/papermerge/supervisord.conf
;;
server_without_init)
# TODO: replace roco with env2js
roco > /usr/share/nginx/html/auth_server/papermerge-runtime-config.js
# Once user options endpoint is implemented, following two lines will removed
/bin/env2js -f /core_app/core.js.tmpl > /usr/share/nginx/html/ui/papermerge-runtime-config.js
sed -i '/Papermerge/a <script type="module" src="/papermerge-runtime-config.js"></script>' /usr/share/nginx/html/ui/index.html
exec /usr/bin/supervisord -c /etc/papermerge/supervisord.conf
;;
create_token.sh)
Expand Down
2 changes: 2 additions & 0 deletions papermerge/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from papermerge.core.features.liveness_probe.router import router as probe_router
from papermerge.search.routers.search import router as search_router
from papermerge.core.features.tasks.router import router as tasks_router

from papermerge.core.version import __version__
from papermerge.core.config import get_settings
Expand Down Expand Up @@ -49,6 +50,7 @@
app.include_router(tags_router, prefix=prefix)
app.include_router(groups_router, prefix=prefix)
app.include_router(probe_router, prefix=prefix)
app.include_router(tasks_router, prefix=prefix)

if config.papermerge__search__url:
app.include_router(search_router, prefix=prefix)
Expand Down
2 changes: 1 addition & 1 deletion papermerge/core/features/nodes/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def create_node(
# if user does not specify document's language, get that
# value from user preferences
if pynode.lang is None:
pynode.lang = settings.papermerge__ocr__default_language
pynode.lang = settings.papermerge__ocr__default_lang_code

attrs = dict(
title=pynode.title,
Expand Down
35 changes: 35 additions & 0 deletions papermerge/core/features/tasks/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Annotated

from fastapi import APIRouter, Security

from papermerge.core import constants, schema, utils
from papermerge.core.features.auth import get_current_user, scopes
from papermerge.core import tasks

from .schema import OCRTaskIn

router = APIRouter(
prefix="/tasks",
tags=["tasks"],
)


@router.post("/ocr")
@utils.docstring_parameter(scope=scopes.TASK_OCR)
def start_ocr(
ocr_task: OCRTaskIn,
user: Annotated[schema.User, Security(get_current_user, scopes=[scopes.TASK_OCR])],
):
"""Triggers OCR for specific document

Required scope: `{scope}`
"""

tasks.send_task(
constants.WORKER_OCR_DOCUMENT,
kwargs={
"document_id": str(ocr_task.document_id),
"lang": ocr_task.lang,
},
route_name="ocr",
)
34 changes: 34 additions & 0 deletions papermerge/core/features/tasks/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Literal
from uuid import UUID

from pydantic import BaseModel

LangCode = Literal[
"ces",
"dan",
"deu",
"ell",
"eng",
"fas",
"fin",
"fra",
"guj",
"heb",
"hin",
"ita",
"jpn",
"kor",
"lit",
"nld",
"nor",
"pol",
"por",
"ron",
"san",
"spa",
]


class OCRTaskIn(BaseModel):
document_id: UUID # document model ID
lang: LangCode
42 changes: 0 additions & 42 deletions papermerge/core/routers/tasks.py

This file was deleted.

38 changes: 0 additions & 38 deletions papermerge/core/schemas/tasks.py

This file was deleted.

1 change: 0 additions & 1 deletion ui2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<body>
<div id="root"></div>
<div id="modals"></div>
<script type="module" src="/papermerge-runtime-config.js"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
5 changes: 0 additions & 5 deletions ui2/papermerge-runtime-config.js

This file was deleted.

38 changes: 38 additions & 0 deletions ui2/src/components/ScheduleOCRProcess.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {useRuntimeConfig} from "@/hooks/runtime_config"
import {OCRCode} from "@/types/ocr"
import {langCodes2ComboboxData} from "@/utils"
import {Select, Stack} from "@mantine/core"
import {useState} from "react"

interface Args {
defaultLang: OCRCode
onLangChange: (newLang: OCRCode) => void
}

export default function ScheduleOCRProcessCheckbox({
defaultLang,
onLangChange
}: Args) {
const runtimeConfig = useRuntimeConfig()
const langData = langCodes2ComboboxData(runtimeConfig.ocr__lang_codes)
const [lang, setLang] = useState<OCRCode>(defaultLang)

const onLangChangeLocal = (value: string | null) => {
if (value) {
setLang(value as OCRCode)
onLangChange(value as OCRCode)
}
}

return (
<Stack my={"md"}>
<Select
searchable
defaultValue={defaultLang}
onChange={onLangChangeLocal}
data={langData}
value={lang}
/>
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {OCR_LANG} from "@/cconstants"
import {useRuntimeConfig} from "@/hooks/runtime_config"
import {OCRCode} from "@/types/ocr"
import {Checkbox, ComboboxData, Select, Stack} from "@mantine/core"
import {langCodes2ComboboxData} from "@/utils"
import {Checkbox, Select, Stack} from "@mantine/core"
import {useState} from "react"

interface Args {
Expand Down Expand Up @@ -55,31 +55,3 @@ export default function ScheduleOCRProcessCheckbox({
</Stack>
)
}

function langCodes2ComboboxData(langCodes: string): ComboboxData {
/*
Input/Output examples:
example 1:

input: "deu,eng,ron"
output: [
{value: "deu", label: "Deutsch"},
{value: "eng", label: "English"},
{value: "ron", label: "Română"}
]

example 2:

input: "fra,spa"
output: [
{value: "fra", label: "Français"},
{value: "spa", label: "Español"},
]
*/
return langCodes
.split(",")
.map(v => v.trim())
.map(v => {
return {value: v, label: OCR_LANG[v] || "Unknown Code"}
})
}
4 changes: 4 additions & 0 deletions ui2/src/features/document/components/ActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {useAppDispatch, useAppSelector} from "@/app/hooks"
import ToggleSecondaryPanel from "@/components/DualPanel/ToggleSecondaryPanel"
import PanelContext from "@/contexts/PanelContext"
import {updateActionPanel} from "@/features/ui/uiSlice"
import {useRuntimeConfig} from "@/hooks/runtime_config"
import {Group} from "@mantine/core"
import {useViewportSize} from "@mantine/hooks"
import {useContext, useEffect, useRef} from "react"
Expand All @@ -14,13 +15,15 @@ import type {PanelMode} from "@/types"
import DownloadButton from "./DownloadButton/DownloadButton"
import RotateButton from "./RotateButton"
import RotateCCButton from "./RotateCCButton"
import RunOCRButton from "./RunOCRButton"

export default function ActionButtons() {
const {height, width} = useViewportSize()
const dispatch = useAppDispatch()
const ref = useRef<HTMLDivElement>(null)
const mode: PanelMode = useContext(PanelContext)
const selectedPages = useAppSelector(s => selectSelectedPages(s, mode)) || []
const runtimeConfig = useRuntimeConfig()

useEffect(() => {
if (ref?.current) {
Expand All @@ -40,6 +43,7 @@ export default function ActionButtons() {
<Group>
<EditTitleButton />
<DownloadButton />
{!runtimeConfig.ocr__automatic && <RunOCRButton />}
{selectedPages.length > 0 && <RotateButton />}
{selectedPages.length > 0 && <RotateCCButton />}
{selectedPages.length > 0 && <DeletePagesButton />}
Expand Down
46 changes: 46 additions & 0 deletions ui2/src/features/document/components/RunOCRButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {useAppSelector} from "@/app/hooks"

import PanelContext from "@/contexts/PanelContext"
import {selectCurrentNodeID} from "@/features/ui/uiSlice"
import {ActionIcon, Box, Tooltip} from "@mantine/core"
import {useDisclosure} from "@mantine/hooks"
import {IconEye} from "@tabler/icons-react"
import {forwardRef, useContext} from "react"

import type {PanelMode} from "@/types"
import {RunOCRModal} from "./RunOCRModal"

interface Args {
hidden?: boolean
}

const RunOCRButton = forwardRef<HTMLButtonElement, Args>((props, ref) => {
const {hidden} = props
const [opened, {open, close}] = useDisclosure(false)
const mode: PanelMode = useContext(PanelContext)
const currentNodeID = useAppSelector(s => selectCurrentNodeID(s, mode))

return (
<Box>
<Tooltip label="Run OCR" withArrow>
<ActionIcon
style={hidden ? {display: "None"} : {}}
ref={ref}
size={"lg"}
variant="default"
onClick={open}
>
<IconEye stroke={1.4} />
</ActionIcon>
</Tooltip>
<RunOCRModal
opened={opened}
node_id={currentNodeID!}
onSubmit={close}
onCancel={close}
/>
</Box>
)
})

export default RunOCRButton
Loading