Skip to content

Commit

Permalink
updates for OCRWorker (#565)
Browse files Browse the repository at this point in the history
  • Loading branch information
ciur authored Dec 7, 2024
1 parent 8e9fa56 commit 62e9fca
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 19 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ ui2/.vscode
ui2/.pnp.cjs
ui2/.pnp.loader.mjs
ui2/.env.development.local
ui2/public/papermerge-runtime-config.js
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ simplest.yml
ui/public/runtime/config.js
.ruff_cache/
.pytest_cache/
.enwardrc
7 changes: 6 additions & 1 deletion docker/standard/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ ENV CORE_APP=/core_app
ENV PAPERMERGE__DATABASE__URL=sqlite:////db/db.sqlite3
ENV PAPERMERGE__AUTH__USERNAME=admin
ENV [email protected]
ENV PAPERMERGE__OCR__DEFAULT_LANGUAGE=deu
ENV PAPERMERGE__MAIN__API_PREFIX=""
ENV PAPERMERGE__OCR__LANG_CODES="deu,eng,ron"
ENV PAPERMERGE__OCR__DEFAULT_LANG_CODE="deu"
ENV PAPERMERGE__OCR__AUTOMATIC="false"

RUN apk update && apk add linux-headers python3-dev \
gcc \
curl \
libc-dev \
supervisor \
imagemagick \
Expand All @@ -27,6 +30,7 @@ RUN apk update && apk add linux-headers python3-dev \
poppler-utils

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

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

Expand All @@ -37,6 +41,7 @@ COPY docker/standard/entrypoint.sh /entrypoint.sh
COPY docker/standard/bundles/supervisor/* /etc/papermerge/
COPY docker/standard/bundles/nginx/* /etc/nginx/
COPY docker/standard/logging.yaml /etc/papermerge/
COPY docker/standard/core.js.tmpl /${CORE_APP}/core.js.tmpl
COPY ./papermerge ${CORE_APP}/papermerge/
COPY alembic.ini ${CORE_APP}/

Expand Down
5 changes: 5 additions & 0 deletions docker/standard/core.js.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
window.__PAPERMERGE_RUNTIME_CONFIG__ = {
ocr__lang_codes: "{{ .PAPERMERGE__OCR__LANG_CODES }}",
ocr__default_lang_code: "{{ .PAPERMERGE__OCR__DEFAULT_LANG_CODE }}",
ocr__automatic: {{ .PAPERMERGE__OCR__AUTOMATIC }}
}
9 changes: 8 additions & 1 deletion docker/standard/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,15 @@ case $CMD in
;;
server)
exec_init
# TODO: replace roco with env2js
roco > /usr/share/nginx/html/auth_server/papermerge-runtime-config.js
roco > /usr/share/nginx/html/ui/papermerge-runtime-config.js
/bin/env2js -f /core_app/core.js.tmpl > /usr/share/nginx/html/ui/papermerge-runtime-config.js
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
/bin/env2js -f /core_app/core.js.tmpl > /usr/share/nginx/html/ui/papermerge-runtime-config.js
exec /usr/bin/supervisord -c /etc/papermerge/supervisord.conf
;;
create_token.sh)
Expand Down
10 changes: 9 additions & 1 deletion papermerge/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ class Settings(BaseSettings):
papermerge__main__cf_domain: str | None = None
papermerge__database__url: str = "sqlite:////db/db.sqlite3"
papermerge__redis__url: str | None = None
papermerge__ocr__default_language: str = 'deu'
papermerge__ocr__default_lang_code: str = 'deu'
# When is OCR triggered ?
# `ocr__automatic` = True means that OCR will be performed without
# end user intervention i.e. via background scheduler like celery scheduler
# `ocr__automatic` = False means that OCR will be performed only
# if requested by end user. In this case user can choose to
# start schedule OCR on upload; also in this case use can choose to
# scheduler OCR later on any document.
papermerge__ocr__automatic: bool = False
papermerge__search__url: str | None = None


Expand Down
32 changes: 32 additions & 0 deletions papermerge/core/features/document/cli/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import uuid
import typer

from papermerge.core.tasks import send_task
from papermerge.core.db.engine import Session

from papermerge.core import dbapi, constants, types


app = typer.Typer(help="OCR tasks")


@app.command()
def schedule_ocr(node_id: uuid.UUID, force: bool = False, lang: str | None = None):
"""Schedules OCR for given node ID"""
with Session() as db_session:
node_type: types.CType = dbapi.get_node_type(db_session, node_id)

if node_type == "document":
if lang is None:
lang = dbapi.get_document_lang(db_session, node_id)
send_task(
constants.WORKER_OCR_DOCUMENT,
kwargs={
"document_id": str(node_id),
"lang": lang,
},
route_name="ocr",
)
else:
# get all descendants of node_id
pass
14 changes: 13 additions & 1 deletion papermerge/core/features/document/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@
)
from papermerge.core.features.document.schema import DocumentCFVRow
from papermerge.core.features.document.ordered_document_cfv import OrderedDocumentCFV
from papermerge.core import config

from .selectors import select_doc_cfv, select_docs_by_type


settings = config.get_settings()

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -634,6 +635,17 @@ def upload(
route_name="s3",
)

if not settings.papermerge__ocr__automatic:
if doc.ocr is True:
# user chose "schedule OCR" when uploading document
tasks.send_task(
constants.WORKER_OCR_DOCUMENT,
kwargs={
"document_id": str(doc.id),
"lang": doc.lang,
},
route_name="ocr",
)

return validated_model, None

Expand Down
1 change: 1 addition & 0 deletions ui2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<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: 5 additions & 0 deletions ui2/papermerge-runtime-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
window.__PAPERMERGE_RUNTIME_CONFIG__ = {
ocr__lang_codes: "deu,eng,ron, spa, ita, fra",
ocr__default_lang_code: "eng",
ocr__automatic: false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 {useState} from "react"

interface Args {
initialCheckboxValue: boolean
defaultLang: OCRCode
onLangChange: (newLang: OCRCode) => void
onCheckboxChange: (newValue: boolean) => void
}

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

const onCheckboxChangeLocal = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.checked
setChecked(newValue)
onCheckboxChange(newValue)
}
const onLangChangeLocal = (value: string | null) => {
if (value) {
setLang(value as OCRCode)
onLangChange(value as OCRCode)
}
}

return (
<Stack my={"md"}>
<Checkbox
mt="md"
mb="md"
label="Schedule OCR processing"
checked={checked}
onChange={onCheckboxChangeLocal}
/>
{checked && (
<Select
searchable
defaultValue={defaultLang}
onChange={onLangChangeLocal}
data={langData}
value={lang}
/>
)}
</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"}
})
}
Empty file.
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import {
Button,
Checkbox,
Container,
Group,
Loader,
Modal,
Text
} from "@mantine/core"
import {Button, Container, Group, Loader, Modal, Text} from "@mantine/core"
import {useState} from "react"

import {useAppDispatch} from "@/app/hooks"
import {apiSlice} from "@/features/api/slice"
import {uploadFile} from "@/features/nodes/uploadFile"

import Error from "@/components/Error"
import type {FolderType} from "@/types"
import ScheduleOCRProcessCheckbox from "@/components/ScheduleOCRProcessCheckbox/ScheduleOCRProcessCheckbox"
import {useRuntimeConfig} from "@/hooks/runtime_config"
import type {FolderType, OCRCode} from "@/types"

type Args = {
opened: boolean
Expand All @@ -34,18 +28,30 @@ export const DropFilesModal = ({
if (!source_files) {
return
}
const runtimeConfig = useRuntimeConfig()
const dispatch = useAppDispatch()
const [error, setError] = useState("")
const [scheduleOCR, setScheduleOCR] = useState<boolean>(false)
const [lang, setLang] = useState<OCRCode>("deu")
const source_titles = [...source_files].map(n => n.name).join(", ")
const target_title = target.title

const onLangChange = (newLang: OCRCode) => {
setLang(newLang)
}

const onCheckboxChange = (newValue: boolean) => {
setScheduleOCR(newValue)
}

const localSubmit = async () => {
for (let i = 0; i < source_files.length; i++) {
dispatch(
uploadFile({
file: source_files[i],
refreshTarget: true,
skipOCR: false,
ocr: scheduleOCR,
lang: lang,
target
})
).then(() => {
Expand Down Expand Up @@ -74,7 +80,14 @@ export const DropFilesModal = ({
{` ${target_title}`}
</Text>
?
<Checkbox mt="md" mb="md" label="Skip OCR" />
{!runtimeConfig.ocr__automatic && (
<ScheduleOCRProcessCheckbox
initialCheckboxValue={false}
defaultLang={runtimeConfig.ocr__default_lang_code}
onCheckboxChange={onCheckboxChange}
onLangChange={onLangChange}
/>
)}
{error && <Error message={error} />}
<Group gap="lg" justify="space-between">
<Button variant="default" onClick={localSubmit}>
Expand Down
9 changes: 6 additions & 3 deletions ui2/src/features/nodes/uploadFile.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import {createAsyncThunk} from "@reduxjs/toolkit"

import {uploaderFileItemUpdated} from "@/features/ui/uiSlice"
import type {FolderType, NodeType} from "@/types"
import type {FolderType, NodeType, OCRCode} from "@/types"
import {getBaseURL, getDefaultHeaders} from "@/utils"
import axios from "axios"

type UploadFileInput = {
file: File
refreshTarget: boolean
target: FolderType
skipOCR: boolean
ocr: boolean
lang: OCRCode
}

type CreateDocumentType = {
title: string
parent_id: string
ctype: "document"
lang: string
ocr: boolean
}

Expand All @@ -38,7 +40,8 @@ export const uploadFile = createAsyncThunk<UploadFileOutput, UploadFileInput>(
title: args.file.name,
parent_id: args.target.id,
ctype: "document",
ocr: !args.skipOCR
lang: args.lang,
ocr: args.ocr
}

thunkApi.dispatch(
Expand Down
20 changes: 20 additions & 0 deletions ui2/src/hooks/runtime_config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {RuntimeConfig} from "@/types/runtime_config"
import {useEffect, useState} from "react"

const RUNTIME_CONFIG_DEFAULT: RuntimeConfig = {
ocr__lang_codes: "deu,eng,ron",
ocr__default_lang_code: "deu",
ocr__automatic: false
}

export function useRuntimeConfig(): RuntimeConfig {
const [config, setConfig] = useState<RuntimeConfig>(RUNTIME_CONFIG_DEFAULT)

useEffect(() => {
if (window.hasOwnProperty("__PAPERMERGE_RUNTIME_CONFIG__")) {
setConfig(window.__PAPERMERGE_RUNTIME_CONFIG__)
}
}, [JSON.stringify(window.__PAPERMERGE_RUNTIME_CONFIG__)])

return config
}
13 changes: 13 additions & 0 deletions ui2/src/types/runtime_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {OCRCode} from "@/types"

export type RuntimeConfig = {
ocr__lang_codes: string
ocr__default_lang_code: OCRCode
ocr__automatic: boolean
}

declare global {
interface Window {
__PAPERMERGE_RUNTIME_CONFIG__: RuntimeConfig
}
}

0 comments on commit 62e9fca

Please sign in to comment.