datashare-passport-worker 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datashare_passport_worker-0.1.2/.gitignore +21 -0
- datashare_passport_worker-0.1.2/PKG-INFO +14 -0
- datashare_passport_worker-0.1.2/README.md +0 -0
- datashare_passport_worker-0.1.2/passport_worker/__init__.py +0 -0
- datashare_passport_worker-0.1.2/passport_worker/activities.py +305 -0
- datashare_passport_worker-0.1.2/passport_worker/aggregate.py +44 -0
- datashare_passport_worker-0.1.2/passport_worker/config.py +82 -0
- datashare_passport_worker-0.1.2/passport_worker/constants.py +6 -0
- datashare_passport_worker-0.1.2/passport_worker/dependencies.py +67 -0
- datashare_passport_worker-0.1.2/passport_worker/inference.py +395 -0
- datashare_passport_worker-0.1.2/passport_worker/objects.py +302 -0
- datashare_passport_worker-0.1.2/passport_worker/preprocessing.py +319 -0
- datashare_passport_worker-0.1.2/passport_worker/search.py +193 -0
- datashare_passport_worker-0.1.2/passport_worker/utils.py +53 -0
- datashare_passport_worker-0.1.2/passport_worker/workflows.py +190 -0
- datashare_passport_worker-0.1.2/pyproject.toml +84 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datashare-passport-worker
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Datashare passport detection worker
|
|
5
|
+
Author-email: Clément Doumouro <cdoumouro@icij.org>, Clément Doumouro <clement.doumouro@gmail.com>
|
|
6
|
+
Requires-Python: <3.14,>=3.12
|
|
7
|
+
Requires-Dist: datashare-python~=0.10.0
|
|
8
|
+
Requires-Dist: icij-common[elasticsearch]~=0.8.3
|
|
9
|
+
Provides-Extra: inference-cpu
|
|
10
|
+
Requires-Dist: icij-passport-core[cpu,inference]==0.10.9; extra == 'inference-cpu'
|
|
11
|
+
Provides-Extra: inference-gpu
|
|
12
|
+
Requires-Dist: icij-passport-core[gpu,inference]==0.10.9; extra == 'inference-gpu'
|
|
13
|
+
Provides-Extra: preprocessing
|
|
14
|
+
Requires-Dist: icij-passport-core[preprocessing]==0.10.9; extra == 'preprocessing'
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from functools import partial
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Annotated, cast
|
|
6
|
+
|
|
7
|
+
from aiofile import async_open
|
|
8
|
+
from datashare_python.dependencies import lifespan_es_client, lifespan_worker_config
|
|
9
|
+
from datashare_python.types_ import (
|
|
10
|
+
AsyncProgressRateHandler,
|
|
11
|
+
SyncProgressRateHandler,
|
|
12
|
+
Weight,
|
|
13
|
+
)
|
|
14
|
+
from datashare_python.utils import (
|
|
15
|
+
ActivityWithProgress,
|
|
16
|
+
activity_defn,
|
|
17
|
+
activity_workdir,
|
|
18
|
+
async_enter_cm,
|
|
19
|
+
config_cache_key,
|
|
20
|
+
enter_cm,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .aggregate import aggregate_results_act
|
|
24
|
+
from .config import PassportWorkerConfig
|
|
25
|
+
from .dependencies import (
|
|
26
|
+
lifespan_image_preprocessor_cache,
|
|
27
|
+
lifespan_passport_detector_cache,
|
|
28
|
+
lifespan_pdf_converter_cache,
|
|
29
|
+
)
|
|
30
|
+
from .inference import (
|
|
31
|
+
PassportDetector,
|
|
32
|
+
create_inference_batches_act,
|
|
33
|
+
detect_passports_act,
|
|
34
|
+
)
|
|
35
|
+
from .objects import (
|
|
36
|
+
DocId,
|
|
37
|
+
DocumentSearchQuery,
|
|
38
|
+
ImagePreprocessorConfig,
|
|
39
|
+
PassportDetectionArgs,
|
|
40
|
+
PassportDetectionResponse,
|
|
41
|
+
PDFConverterConfig,
|
|
42
|
+
PreprocessingBatches,
|
|
43
|
+
)
|
|
44
|
+
from .preprocessing import (
|
|
45
|
+
ImagePreprocessor,
|
|
46
|
+
PDFConverter,
|
|
47
|
+
convert_to_pdfs_act,
|
|
48
|
+
preprocess_images_act,
|
|
49
|
+
preprocess_pdfs_act,
|
|
50
|
+
)
|
|
51
|
+
from .search import create_preprocessing_batches_act
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
_BASE_WEIGHT = 1.0
|
|
56
|
+
_CREATE_PREPROCESSING_BATCHES_WEIGHT = _BASE_WEIGHT * 1
|
|
57
|
+
_PREPROCESS_IMAGES_WEIGHT = _BASE_WEIGHT * 5
|
|
58
|
+
_CONVERT_TO_PDF_WEIGHT = _BASE_WEIGHT * 7
|
|
59
|
+
_PREPROCESS_PDF_WEIGHT = _BASE_WEIGHT * 3
|
|
60
|
+
_CREATE_INFERENCE_BATCH_WEIGHT = _CREATE_PREPROCESSING_BATCHES_WEIGHT * 1
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Activity(StrEnum):
|
|
64
|
+
CREATE_PREPROCESSING_BATCHES = "passport-detection.create-preprocessing-batches"
|
|
65
|
+
PREPROCESS_IMAGES = "passport-detection.preprocess.images"
|
|
66
|
+
CONVERT_TO_PDFS = "passport-detection.convert-to-pdf"
|
|
67
|
+
PREPROCESS_PDFS = "passport-detection.preprocess.pdfs"
|
|
68
|
+
CREATE_INFERENCE_BATCHES = "passport-detection.create-inference-batches"
|
|
69
|
+
DETECT_PASSPORTS = "passport-detection.detect-passports"
|
|
70
|
+
AGGREGATE_RESULTS = "passport-detection.aggregate-results"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PassportDetectionActivities(ActivityWithProgress):
|
|
74
|
+
@activity_defn(name=Activity.CREATE_PREPROCESSING_BATCHES)
|
|
75
|
+
async def create_preprocessing_batches(
|
|
76
|
+
self,
|
|
77
|
+
docs: list[DocId] | DocumentSearchQuery | None,
|
|
78
|
+
project: str,
|
|
79
|
+
*,
|
|
80
|
+
progress: Annotated[ # noqa: ARG002
|
|
81
|
+
AsyncProgressRateHandler | None,
|
|
82
|
+
Weight(value=_CREATE_PREPROCESSING_BATCHES_WEIGHT),
|
|
83
|
+
] = None,
|
|
84
|
+
) -> PreprocessingBatches:
|
|
85
|
+
es_client = lifespan_es_client()
|
|
86
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
87
|
+
workdir = worker_config.paths.workdir
|
|
88
|
+
output_root = activity_workdir(workdir, project, act_context=True)
|
|
89
|
+
output_root.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
target_n_pages_per_batch = worker_config.preprocessing.target_n_pages_per_batch
|
|
91
|
+
return await create_preprocessing_batches_act(
|
|
92
|
+
docs,
|
|
93
|
+
project,
|
|
94
|
+
es_client,
|
|
95
|
+
worker_config.paths,
|
|
96
|
+
target_n_pages_per_batch,
|
|
97
|
+
output_root=output_root,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@activity_defn(name=Activity.PREPROCESS_IMAGES)
|
|
101
|
+
def preprocess_images(
|
|
102
|
+
self,
|
|
103
|
+
batch: Path,
|
|
104
|
+
project: str,
|
|
105
|
+
config: ImagePreprocessorConfig,
|
|
106
|
+
*,
|
|
107
|
+
progress: Annotated[
|
|
108
|
+
SyncProgressRateHandler | None, Weight(value=_PREPROCESS_IMAGES_WEIGHT)
|
|
109
|
+
] = None,
|
|
110
|
+
) -> tuple[Path, Path]:
|
|
111
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
112
|
+
workdir = worker_config.paths.workdir
|
|
113
|
+
logger.info("loading image preprocessor...")
|
|
114
|
+
cache = lifespan_image_preprocessor_cache()
|
|
115
|
+
image_preprocessor_cache_key = config_cache_key(config)
|
|
116
|
+
image_preprocessor_factory = enter_cm(
|
|
117
|
+
partial(ImagePreprocessor.from_config, config)
|
|
118
|
+
)
|
|
119
|
+
image_preprocessor = cache.get_or_cache_resource(
|
|
120
|
+
image_preprocessor_cache_key, image_preprocessor_factory
|
|
121
|
+
)
|
|
122
|
+
logger.info("loaded image preprocessor !")
|
|
123
|
+
pages_root = activity_workdir(workdir, project, act_context=False)
|
|
124
|
+
pages_root.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
executor = worker_config.to_image_preprocessing_executor()
|
|
126
|
+
chunk_size = worker_config.preprocessing.images.chunk_size
|
|
127
|
+
success, errors = preprocess_images_act(
|
|
128
|
+
batch,
|
|
129
|
+
worker_config.paths,
|
|
130
|
+
output_root=pages_root,
|
|
131
|
+
image_preprocessor=image_preprocessor,
|
|
132
|
+
executor=executor,
|
|
133
|
+
chunk_size=chunk_size,
|
|
134
|
+
event_loop=self._event_loop,
|
|
135
|
+
progress=progress,
|
|
136
|
+
)
|
|
137
|
+
res_root = activity_workdir(workdir, project, act_context=True)
|
|
138
|
+
res_root.mkdir(parents=True, exist_ok=True)
|
|
139
|
+
successes_path = res_root / "pages.jsonl"
|
|
140
|
+
successes_path.write_text("\n".join(p.model_dump_json() for p in success))
|
|
141
|
+
errors_path = res_root / "errors.jsonl"
|
|
142
|
+
errors_path.write_text("\n".join(p.model_dump_json() for p in errors))
|
|
143
|
+
return successes_path, errors_path
|
|
144
|
+
|
|
145
|
+
@activity_defn(name=Activity.CONVERT_TO_PDFS)
|
|
146
|
+
async def convert_to_pdfs(
|
|
147
|
+
self,
|
|
148
|
+
batch: Path,
|
|
149
|
+
project: str,
|
|
150
|
+
config: PDFConverterConfig,
|
|
151
|
+
*,
|
|
152
|
+
progress: Annotated[
|
|
153
|
+
AsyncProgressRateHandler | None, Weight(value=_CONVERT_TO_PDF_WEIGHT)
|
|
154
|
+
] = None,
|
|
155
|
+
) -> tuple[Path, Path]:
|
|
156
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
157
|
+
cache = lifespan_pdf_converter_cache()
|
|
158
|
+
pdf_converter_cache_key = config_cache_key(config)
|
|
159
|
+
pdf_converter_factory = async_enter_cm(
|
|
160
|
+
partial(PDFConverter.from_config, config)
|
|
161
|
+
)
|
|
162
|
+
pdf_converter = await cache.async_get_or_cache_resource(
|
|
163
|
+
pdf_converter_cache_key, pdf_converter_factory
|
|
164
|
+
)
|
|
165
|
+
workdir = worker_config.paths.workdir
|
|
166
|
+
pdfs_root = activity_workdir(workdir, project, act_context=False)
|
|
167
|
+
pdfs_root.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
max_concurrency = worker_config.preprocessing.pdfs.max_concurrency
|
|
169
|
+
successes, errors = await convert_to_pdfs_act(
|
|
170
|
+
batch,
|
|
171
|
+
pdf_converter,
|
|
172
|
+
worker_config.paths,
|
|
173
|
+
max_concurrency,
|
|
174
|
+
output_root=pdfs_root,
|
|
175
|
+
progress=progress,
|
|
176
|
+
)
|
|
177
|
+
res_root = activity_workdir(workdir, project, act_context=True)
|
|
178
|
+
res_root.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
pdf_paths = res_root / "pdfs.jsonl"
|
|
180
|
+
async with async_open(pdf_paths, "w") as f:
|
|
181
|
+
await f.write("\n".join(d.model_dump_json() for d in successes))
|
|
182
|
+
errors_path = res_root / "errors.jsonl"
|
|
183
|
+
async with async_open(errors_path, "w") as f:
|
|
184
|
+
await f.write("\n".join(e.model_dump_json() for e in errors))
|
|
185
|
+
return pdf_paths, errors_path
|
|
186
|
+
|
|
187
|
+
@activity_defn(name=Activity.PREPROCESS_PDFS)
|
|
188
|
+
async def preprocess_pdfs(
|
|
189
|
+
self,
|
|
190
|
+
batch: Path,
|
|
191
|
+
project: str,
|
|
192
|
+
*,
|
|
193
|
+
progress: Annotated[
|
|
194
|
+
AsyncProgressRateHandler | None, Weight(value=_PREPROCESS_PDF_WEIGHT)
|
|
195
|
+
] = None,
|
|
196
|
+
) -> tuple[Path, Path]:
|
|
197
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
198
|
+
workdir = worker_config.paths.workdir
|
|
199
|
+
output_root = activity_workdir(workdir, project, act_context=False)
|
|
200
|
+
output_root.mkdir(parents=True, exist_ok=True)
|
|
201
|
+
successes, errors = await preprocess_pdfs_act(
|
|
202
|
+
batch, worker_config.paths, output_root=output_root, progress=progress
|
|
203
|
+
)
|
|
204
|
+
res_root = activity_workdir(workdir, project, act_context=True)
|
|
205
|
+
res_root.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
pdf_paths = res_root / "pdfs.jsonl"
|
|
207
|
+
async with async_open(pdf_paths, "w") as f:
|
|
208
|
+
await f.write("\n".join(d.model_dump_json() for d in successes))
|
|
209
|
+
errors_path = res_root / "errors.jsonl"
|
|
210
|
+
async with async_open(errors_path, "w") as f:
|
|
211
|
+
await f.write("\n".join(e.model_dump_json() for e in errors))
|
|
212
|
+
return pdf_paths, errors_path
|
|
213
|
+
|
|
214
|
+
@activity_defn(name=Activity.CREATE_INFERENCE_BATCHES)
|
|
215
|
+
async def create_inference_batches(
|
|
216
|
+
self,
|
|
217
|
+
batches: list[Path],
|
|
218
|
+
project: str,
|
|
219
|
+
*,
|
|
220
|
+
progress: Annotated[ # noqa:ARG002
|
|
221
|
+
AsyncProgressRateHandler | None,
|
|
222
|
+
Weight(value=_CREATE_INFERENCE_BATCH_WEIGHT),
|
|
223
|
+
] = None,
|
|
224
|
+
) -> list[Path]:
|
|
225
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
226
|
+
batch_size = worker_config.inference.batch_size
|
|
227
|
+
batches_per_task = worker_config.inference.batches_per_task
|
|
228
|
+
worker_paths = worker_config.paths
|
|
229
|
+
output_root = activity_workdir(worker_paths.workdir, project)
|
|
230
|
+
output_root.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
return await create_inference_batches_act(
|
|
232
|
+
batches,
|
|
233
|
+
worker_paths,
|
|
234
|
+
output_root,
|
|
235
|
+
target_batches_per_task=batches_per_task,
|
|
236
|
+
inference_batch_size=batch_size,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
@activity_defn(name=Activity.DETECT_PASSPORTS)
|
|
240
|
+
async def detect_passports(
|
|
241
|
+
self,
|
|
242
|
+
batch: Path,
|
|
243
|
+
args: PassportDetectionArgs,
|
|
244
|
+
*,
|
|
245
|
+
progress: Annotated[
|
|
246
|
+
AsyncProgressRateHandler | None,
|
|
247
|
+
Weight(value=_CREATE_INFERENCE_BATCH_WEIGHT),
|
|
248
|
+
] = None,
|
|
249
|
+
) -> Path:
|
|
250
|
+
logger.info("loading passport detector...")
|
|
251
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
252
|
+
batch_size = worker_config.inference.batch_size
|
|
253
|
+
cache = lifespan_passport_detector_cache()
|
|
254
|
+
passport_detector_config = args.config.inference.passport_detector
|
|
255
|
+
passport_detector_key = config_cache_key(passport_detector_config)
|
|
256
|
+
passport_detector_factory = enter_cm(
|
|
257
|
+
partial(PassportDetector.from_config, passport_detector_config)
|
|
258
|
+
)
|
|
259
|
+
passport_detector = cache.get_or_cache_resource(
|
|
260
|
+
passport_detector_key, passport_detector_factory
|
|
261
|
+
)
|
|
262
|
+
logger.info("passport detector loaded !")
|
|
263
|
+
workdir = worker_config.paths.workdir
|
|
264
|
+
res_root = activity_workdir(workdir, args.project, act_context=True)
|
|
265
|
+
res_root.mkdir(parents=True, exist_ok=True)
|
|
266
|
+
res = await detect_passports_act(
|
|
267
|
+
batch,
|
|
268
|
+
passport_detector,
|
|
269
|
+
worker_config.paths,
|
|
270
|
+
args,
|
|
271
|
+
batch_size=batch_size,
|
|
272
|
+
progress=progress,
|
|
273
|
+
)
|
|
274
|
+
result_path = res_root / "inference_results.json"
|
|
275
|
+
async with async_open(result_path, "w") as f:
|
|
276
|
+
await f.write(res.model_dump_json())
|
|
277
|
+
return result_path
|
|
278
|
+
|
|
279
|
+
@activity_defn(name=Activity.AGGREGATE_RESULTS)
|
|
280
|
+
async def aggregate_results(
|
|
281
|
+
self,
|
|
282
|
+
error_paths: list[Path],
|
|
283
|
+
result_paths: list[Path],
|
|
284
|
+
*,
|
|
285
|
+
progress: Annotated[ # noqa:ARG002
|
|
286
|
+
AsyncProgressRateHandler | None,
|
|
287
|
+
Weight(value=_CREATE_INFERENCE_BATCH_WEIGHT),
|
|
288
|
+
] = None,
|
|
289
|
+
) -> PassportDetectionResponse:
|
|
290
|
+
worker_config = cast(PassportWorkerConfig, lifespan_worker_config())
|
|
291
|
+
res = await aggregate_results_act(
|
|
292
|
+
error_paths, result_paths=result_paths, paths=worker_config.paths
|
|
293
|
+
)
|
|
294
|
+
return res
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
ACTIVITIES = [
|
|
298
|
+
PassportDetectionActivities.create_preprocessing_batches,
|
|
299
|
+
PassportDetectionActivities.preprocess_images,
|
|
300
|
+
PassportDetectionActivities.convert_to_pdfs,
|
|
301
|
+
PassportDetectionActivities.preprocess_pdfs,
|
|
302
|
+
PassportDetectionActivities.create_inference_batches,
|
|
303
|
+
PassportDetectionActivities.detect_passports,
|
|
304
|
+
PassportDetectionActivities.aggregate_results,
|
|
305
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import AsyncIterable
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
from datashare_python.objects import WorkerPaths
|
|
7
|
+
from datashare_python.utils import async_read_jsonl_as
|
|
8
|
+
|
|
9
|
+
from passport_worker.objects import (
|
|
10
|
+
FileProcessingError,
|
|
11
|
+
PartialDetectionResult,
|
|
12
|
+
PassportDetectionResponse,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def aggregate_results_act(
|
|
17
|
+
error_paths: list[Path], *, result_paths: list[Path], paths: WorkerPaths
|
|
18
|
+
) -> PassportDetectionResponse:
|
|
19
|
+
workdir = paths.workdir
|
|
20
|
+
preprocessing_errors = await asyncio.gather(
|
|
21
|
+
*(
|
|
22
|
+
_as_list(async_read_jsonl_as(workdir / p, FileProcessingError))
|
|
23
|
+
for p in error_paths
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
preprocessing_errors = sum(preprocessing_errors, [])
|
|
27
|
+
inference_results = await asyncio.gather(
|
|
28
|
+
*(
|
|
29
|
+
_as_list(async_read_jsonl_as(workdir / p, PartialDetectionResult))
|
|
30
|
+
for p in result_paths
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
inference_results = sum(inference_results, [])
|
|
34
|
+
res = PassportDetectionResponse.aggregate(
|
|
35
|
+
preprocessing_errors, inference_results=inference_results
|
|
36
|
+
)
|
|
37
|
+
return res
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
T = TypeVar("T")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def _as_list[T](iterable: AsyncIterable[T]) -> list[T]:
|
|
44
|
+
return [i async for i in iterable]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
2
|
+
|
|
3
|
+
import datashare_python
|
|
4
|
+
from datashare_python.config import (
|
|
5
|
+
LogFormat,
|
|
6
|
+
LoggingConfig,
|
|
7
|
+
ResourceCacheConfig,
|
|
8
|
+
WorkerConfig,
|
|
9
|
+
)
|
|
10
|
+
from datashare_python.objects import DatashareModel, WorkerPaths
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
|
|
13
|
+
_ALL_LOGGERS = [datashare_python.__name__, __name__, "__main__"]
|
|
14
|
+
|
|
15
|
+
_DEFAULT_LOGGERS = {
|
|
16
|
+
datashare_python.__name__: "INFO",
|
|
17
|
+
__name__: "INFO",
|
|
18
|
+
"__main__": "INFO",
|
|
19
|
+
"passport_service": "INFO",
|
|
20
|
+
}
|
|
21
|
+
_DEFAULT_LOGGING_CONFIG = LoggingConfig(
|
|
22
|
+
format=LogFormat.DEFAULT, loggers=_DEFAULT_LOGGERS
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ImagePreprocessingWorkerConfig(DatashareModel):
|
|
27
|
+
n_processes: int | None = None
|
|
28
|
+
chunk_size: int = 5
|
|
29
|
+
|
|
30
|
+
def to_image_preprocessing_executor(self) -> ProcessPoolExecutor:
|
|
31
|
+
return ProcessPoolExecutor(max_workers=self.n_processes)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PDFConversionWorkerConfig(DatashareModel):
|
|
35
|
+
max_concurrency: int = 10
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PreprocessingWorkerConfig(DatashareModel):
|
|
39
|
+
target_n_pages_per_batch: int = 200
|
|
40
|
+
|
|
41
|
+
images: ImagePreprocessingWorkerConfig = Field(
|
|
42
|
+
default_factory=ImagePreprocessingWorkerConfig
|
|
43
|
+
)
|
|
44
|
+
pdfs: PDFConversionWorkerConfig = Field(default_factory=PDFConversionWorkerConfig)
|
|
45
|
+
|
|
46
|
+
def to_image_preprocessing_executor(self) -> ProcessPoolExecutor:
|
|
47
|
+
return self.images.to_image_preprocessing_executor()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class InferenceWorkerConfig(DatashareModel):
|
|
51
|
+
batch_size: int = 32
|
|
52
|
+
batches_per_task: int = 5
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class PreprocessingCacheConfig(DatashareModel):
|
|
56
|
+
pdf: ResourceCacheConfig = Field(default_factory=ResourceCacheConfig)
|
|
57
|
+
images: ResourceCacheConfig = Field(default_factory=ResourceCacheConfig)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PassportWorkerCacheConfig(DatashareModel):
|
|
61
|
+
preprocessing: PreprocessingCacheConfig = Field(
|
|
62
|
+
default_factory=PreprocessingCacheConfig
|
|
63
|
+
)
|
|
64
|
+
inference: ResourceCacheConfig = Field(default_factory=ResourceCacheConfig)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PassportWorkerConfig(WorkerConfig):
|
|
68
|
+
logging: LoggingConfig = _DEFAULT_LOGGING_CONFIG
|
|
69
|
+
paths: WorkerPaths
|
|
70
|
+
|
|
71
|
+
cache: PassportWorkerCacheConfig = Field(default_factory=PassportWorkerCacheConfig)
|
|
72
|
+
|
|
73
|
+
preprocessing: PreprocessingWorkerConfig = Field(
|
|
74
|
+
default_factory=PreprocessingWorkerConfig
|
|
75
|
+
)
|
|
76
|
+
inference: InferenceWorkerConfig = Field(default_factory=InferenceWorkerConfig)
|
|
77
|
+
|
|
78
|
+
def to_image_preprocessing_executor(self) -> ProcessPoolExecutor:
|
|
79
|
+
return self.preprocessing.to_image_preprocessing_executor()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
WORKER_CONFIG_CLS = PassportWorkerConfig
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from contextvars import ContextVar
|
|
2
|
+
|
|
3
|
+
from datashare_python.dependencies import set_es_client, set_loggers, set_worker_config
|
|
4
|
+
from datashare_python.exceptions import DependencyInjectionError
|
|
5
|
+
from datashare_python.utils import SharedResources
|
|
6
|
+
|
|
7
|
+
from passport_worker.config import PassportWorkerConfig
|
|
8
|
+
|
|
9
|
+
_IMAGE_PREPROCESSOR_CACHE: ContextVar[SharedResources] = ContextVar(
|
|
10
|
+
"image_preprocessor_cache"
|
|
11
|
+
)
|
|
12
|
+
_PDF_CONVERTER_CACHE: ContextVar[SharedResources] = ContextVar("pdf_converter_cache")
|
|
13
|
+
_PASSPORT_DETECTOR_CACHE: ContextVar[SharedResources] = ContextVar("preprocessor_cache")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def set_image_preprocessor_cache(
|
|
17
|
+
worker_config: PassportWorkerConfig,
|
|
18
|
+
) -> SharedResources:
|
|
19
|
+
cache = worker_config.cache.preprocessing.images.to_resource_cache()
|
|
20
|
+
_IMAGE_PREPROCESSOR_CACHE.set(cache)
|
|
21
|
+
return cache
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def lifespan_image_preprocessor_cache() -> SharedResources:
|
|
25
|
+
try:
|
|
26
|
+
return _IMAGE_PREPROCESSOR_CACHE.get()
|
|
27
|
+
except LookupError as e:
|
|
28
|
+
raise DependencyInjectionError("image preprocessor") from e
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def set_pdf_converter_cache(
|
|
32
|
+
worker_config: PassportWorkerConfig,
|
|
33
|
+
) -> SharedResources:
|
|
34
|
+
cache = worker_config.cache.preprocessing.pdf.to_resource_cache()
|
|
35
|
+
_PDF_CONVERTER_CACHE.set(cache)
|
|
36
|
+
return cache
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def lifespan_pdf_converter_cache() -> SharedResources:
|
|
40
|
+
try:
|
|
41
|
+
return _PDF_CONVERTER_CACHE.get()
|
|
42
|
+
except LookupError as e:
|
|
43
|
+
raise DependencyInjectionError("pdf converter cache") from e
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def set_passport_detector_cache(worker_config: PassportWorkerConfig) -> SharedResources:
|
|
47
|
+
cache = worker_config.cache.inference.to_resource_cache()
|
|
48
|
+
_PASSPORT_DETECTOR_CACHE.set(cache)
|
|
49
|
+
return cache
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def lifespan_passport_detector_cache() -> SharedResources:
|
|
53
|
+
try:
|
|
54
|
+
return _PASSPORT_DETECTOR_CACHE.get()
|
|
55
|
+
except LookupError as e:
|
|
56
|
+
raise DependencyInjectionError("passport detector cache") from e
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
IO = [set_worker_config, set_loggers, set_es_client, set_pdf_converter_cache]
|
|
60
|
+
PREPROCESSING = [set_worker_config, set_loggers, set_image_preprocessor_cache]
|
|
61
|
+
INFERENCE = [set_worker_config, set_loggers, set_passport_detector_cache]
|
|
62
|
+
|
|
63
|
+
DEPENDENCIES = {
|
|
64
|
+
"passport-detection.io": IO,
|
|
65
|
+
"passport-detection.preprocessing": PREPROCESSING,
|
|
66
|
+
"passport-detection.inference": INFERENCE,
|
|
67
|
+
}
|