Completed
Pull Request — master (#90)
by Janis
06:21
created

OcrService::process()   C

Complexity

Conditions 8
Paths 30

Size

Total Lines 47
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8.048

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 20
cts 22
cp 0.9091
rs 5.7377
c 0
b 0
f 0
cc 8
eloc 25
nc 30
nop 2
crap 8.048
1
<?php
2
/**
3
 * Nextcloud - OCR
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Janis Koehr <[email protected]>
9
 * @copyright Janis Koehr 2017
10
 */
11
12
namespace OCA\Ocr\Service;
13
14
use Exception;
15
use OC\Files\View;
16
use OCA\Ocr\Db\FileMapper;
17
use OCA\Ocr\Db\File;
18
use OCA\Ocr\Db\OcrStatus;
19
use OCA\Ocr\Db\OcrStatusMapper;
20
use OCA\Ocr\Db\ShareMapper;
21
use OCP\AppFramework\Db\DoesNotExistException;
22
use OCP\IL10N;
23
use OCP\ILogger;
24
use OCP\ITempManager;
25
26
27
/**
28
 * Class OcrService
29
 * @package OCA\Ocr\Service
30
 */
31
class OcrService {
32
33
	/**
34
	 * @var ILogger
35
	 */
36
	private $logger;
37
38
	/**
39
	 * @var ITempManager
40
	 */
41
	private $tempM;
42
43
	/**
44
	 * @var QueueService
45
	 */
46
	private $queueService;
47
48
	/**
49
	 * @var OcrStatusMapper
50
	 */
51
	private $statusMapper;
52
53
	/**
54
	 * @var FileMapper
55
	 */
56
	private $fileMapper;
57
58
	/**
59
	 * @var ShareMapper
60
	 */
61
	private $shareMapper;
62
63
	/**
64
	 * @var View
65
	 */
66
	private $view;
67
68
	/**
69
	 * @var userId
70
	 */
71
	private $userId;
72
73
	/**
74
	 * @var IL10N
75
	 */
76
	private $l10n;
77
78
	/**
79
	 * Array of allowed MIME types for OCR processing
80
	 */
81
	const ALLOWED_MIMETYPES = ['application/pdf', 'image/png', 'image/jpeg', 'image/tiff', 'image/jp2', 'image/jpm', 'image/jpx', 'image/webp', 'image/gif'];
82
83
	/**
84
	 * The correct MIME type for a PDF file
85
	 */
86
	const MIMETYPE_PDF = 'application/pdf';
87
88
	/**
89
	 * The only allowed image MIME types by tesseract
90
	 */
91
	const MIMETYPES_IMAGE = ['image/png', 'image/jpeg', 'image/tiff', 'image/jp2', 'image/jpm', 'image/jpx', 'image/webp', 'image/gif'];
92
93
	/**
94
	 * OcrService constructor.
95
	 *
96
	 * @param ITempManager $tempManager
97
	 * @param QueueService $queueService
98
	 * @param OcrStatusMapper $mapper
99
	 * @param View $view
100
	 * @param $userId
101
	 * @param IL10N $l10n
102
	 * @param ILogger $logger
103
	 */
104 15
	public function __construct(ITempManager $tempManager, QueueService $queueService, OcrStatusMapper $mapper, FileMapper $fileMapper, ShareMapper $shareMapper, View $view, $userId, IL10N $l10n, ILogger $logger) {
105 15
		$this->logger = $logger;
106 15
		$this->tempM = $tempManager;
107 15
		$this->queueService = $queueService;
108 15
		$this->statusMapper = $mapper;
109 15
		$this->view = $view;
110 15
		$this->userId = $userId;
111 15
		$this->l10n = $l10n;
112 15
		$this->fileMapper = $fileMapper;
113 15
		$this->shareMapper = $shareMapper;
114 15
	}
115
116
	/**
117
	 * Gets the list of all available tesseract-ocr languages.
118
	 * TODO: remove and replace by docker behavior
119
	 * @return string[] Languages
120
	 */
121 4
	public function listLanguages() {
122
		try {
123 4
			$success = -1;
124 4
			$this->logger->debug('Fetching languages. ', ['app' => 'ocr']);
125 4
			exec('tesseract --list-langs 2>&1', $result, $success);
126 4
			$this->logger->debug('Result for list-language command: ' . json_encode($result), ['app' => 'ocr']);
127 4
			if ($success === 0 && count($result) > 0) {
128 4
				if (is_array($result)) {
129 4
					$traineddata = $result;
130
				} else {
131
					throw new NotFoundException($this->l10n->t('No languages found.'));
132
				}
133 4
				$languages = array();
134 4
				array_shift($traineddata); // delete the first element of the array as it is a description of tesseract
135 4
				asort($traineddata); // sort the languages alphabetically
136 4
				foreach ($traineddata as $td) {
137 4
					$tdname = trim($td); // strip whitespaces
138 4
					array_push($languages, $tdname); //add to language list
139
				}
140 4
				$this->logger->debug('Fetched languages: ' . json_encode($languages), ['app' => 'ocr']);
141 4
				return $languages;
142
			} else {
143
				throw new NotFoundException($this->l10n->t('No languages found.'));
144
			}
145
		} catch (Exception $e) {
146
			$this->handleException($e);
147
		}
148
	}
149
150
	/**
151
	 * Processes and prepares the files for OCR.
152
	 * Sends the stuff to the client in order to OCR async.
153
	 *
154
	 * @param string[] $languages
155
	 * @param array $files
156
	 * @return string
157
	 */
158 5
	public function process($languages, $files) {
159
		try {
160 5
			$this->logger->debug('Will now process files: ' . json_encode($files) . ' with languages: ' . json_encode($languages), ['app' => 'ocr']);
161
			// Check if files and language not empty
162
			// TODO: do not check if languages empty: tesseract does not need a language necassarily.
163 5
			if (!empty($files) && !empty($languages) && count(array_diff($languages, $this->listLanguages())) === 0) {
164
165 3
				$fileInfo = $this->buildFileInfo($files);
166
167 2
				foreach ($fileInfo as $fInfo) {
168
					// Check if filelock existing
169
					// TODO: FileLock maybe \OC\Files\View::lockFile()
170
					// Check Shared
171 2
					$source = $fInfo->getPath();
172 2
					if ($this->checkSharedWithInitiator($fInfo)) {
173
						// Shared Item
174 1
						$source = str_replace('home::', '', $fInfo->getStoragename()) . '/' . $source;
175 1
						$target = $this->buildTargetForShared($fInfo);
176
					} else {
177
						// Not Shared
178 1
						$source = $this->userId . '/' . $source;
179 1
						$target = $this->buildTarget($fInfo);
180
					}
181
182
					// create a temp file for ocr processing purposes
183 2
					$tempFile = $this->tempM->getTemporaryFile();
184
185
					// set the running type
186 2
					if ($fInfo->getMimetype() === $this::MIMETYPE_PDF) {
187
						$ftype = 'mypdf';
188
					} else {
189 2
						$ftype = 'tess';
190
					}
191
					// Create status object
192 2
					$status = new OcrStatus('PENDING', $source, $target, $tempFile, $ftype, $this->userId, false);
193
					// Init client and send task / job
194
					// Feed the worker
195 2
					$this->queueService->clientSend($status, $languages, \OC::$SERVERROOT);
196
				}
197 2
				return 'PROCESSING';
198
			} else {
199 2
				throw new NotFoundException($this->l10n->t('Empty parameters passed.'));
200
			}
201 3
		} catch (Exception $e) {
202 3
			$this->handleException($e);
203
		}
204
	}
205
206
	/**
207
	 * A function which returns the JSONResponse for all required status checks and tasks.
208
	 * It will check for already processed, pending and failed ocr tasks and return them as needed.
209
	 *
210
	 * @codeCoverageIgnore
211
	 * @return string
212
	 */
213
	public function status() {
214
		try {
215
			// TODO: release lock
216
217
			// returns user specific processed files
218
			$processed = $this->handleProcessed();
219
220
			$pending = $this->statusMapper->findAllPending($this->userId);
221
222
			// return user specific failed state and set up error
223
			$failed = $this->handleFailed();
224
225
			return ['processed' => count($processed), 'failed' => count($failed), 'pending' => count($pending)];
226
		} catch (Exception $e) {
227
			$this->handleException($e);
228
		}
229
	}
230
231
	/**
232
	 * The command ocr:complete for occ will call this function in order to set the status.
233
	 * the worker should call it automatically after each processing step.
234
	 *
235
	 * @param integer $statusId
236
	 * @param boolean $failed
237
	 * @param string $errorMessage
238
	 */
239 3
	public function complete($statusId, $failed, $errorMessage) {
240
		try {
241 3
			$status = $this->statusMapper->find($statusId);
242 2
			if (!$failed) {
243 1
				$status->setStatus('PROCESSED');
244 1
				$this->statusMapper->update($status);
245
			} else {
246 1
				$status->setStatus('FAILED');
247 1
				$this->statusMapper->update($status);
248 2
				$this->logger->error($errorMessage, ['app' => 'ocr']);
249
			}
250 1
		} catch (Exception $e) {
251 1
			$this->handleException($e);
252
		}
253 2
	}
254
255
	/**
256
	 * The PersonalSettingsController will have the opportunity to delete ocr status objects.
257
	 *
258
	 * @param $statusId
259
	 * @param string $userId
260
	 * @return OcrStatus
261
	 */
262 2
	public function deleteStatus($statusId, $userId) {
263
		try {
264 2
			$status = $this->statusMapper->find($statusId);
265 1
			if ($status->getUserId() !== $userId) {
266
				throw new NotFoundException($this->l10n->t('Cannot delete. Wrong owner.'));
267
			} else {
268 1
				$status = $this->statusMapper->delete($status);
269
			}
270 1
			$status->setTarget($this->removeFileExtension($status));
271 1
			$status->setSource(null);
272 1
			$status->setTempFile(null);
273 1
			$status->setType(null);
274 1
			$status->setErrorDisplayed(null);
275 1
			return $status;
276 1
		} catch (Exception $e) {
277 1
			if ($e instanceof DoesNotExistException) {
0 ignored issues
show
Bug introduced by
The class OCP\AppFramework\Db\DoesNotExistException does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
278 1
				$ex = new NotFoundException($this->l10n->t('Cannot delete. Wrong ID.'));
279 1
				$this->handleException($ex);
280
			} else {
281
				$this->handleException($e);
282
			}
283
		}
284
	}
285
286
	/**
287
	 * Gets all status objects for a specific user in order to list them on the personal settings page.
288
	 *
289
	 * @param string $userId
290
	 * @return array
291
	 */
292 1
	public function getAllForPersonal($userId) {
293
		try {
294 1
			$status = $this->statusMapper->findAll($userId);
295 1
			$statusNew = array();
296 1
			for ($x = 0; $x < count($status); $x++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
297 1
				$newName = $this->removeFileExtension($status[$x]);
298 1
				$status[$x]->setTarget($newName);
299 1
				$status[$x]->setSource(null);
300 1
				$status[$x]->setTempFile(null);
301 1
				$status[$x]->setType(null);
302 1
				$status[$x]->setErrorDisplayed(null);
303 1
				array_push($statusNew, $status[$x]);
304
			}
305 1
			return $statusNew;
306
		} catch (Exception $e) {
307
			$this->handleException($e);
308
		}
309
	}
310
311
	/**
312
	 * Returns a not existing file name for pdf or image processing
313
	 * protected as of testing issues with static methods. (Actually
314
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
315
	 * @codeCoverageIgnore
316
	 *
317
	 * @param File $fileInfo
318
	 * @return string
319
	 */
320
	protected function buildTargetForShared(File $fileInfo) {
321
		try {
322
			$share = $this->shareMapper->find($fileInfo->getFileid(), $this->userId, str_replace('home::', '', $fileInfo->getStoragename()));
323
324
			// get rid of the .png or .pdf and so on
325
			$fileName = substr($share->getFileTarget(), 0 , (strrpos($share->getFileTarget(), '.'))); // '/thedom.png' => '/thedom' || '/Test/thedom.png' => '/Test/thedom'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
326
327
			// remove everything in front of and including of the first appearance of a slash from behind
328
			$fileName = substr(strrchr($fileName, "/"), 1); // '/thedom' => 'thedom' || '/Test/thedom' => 'thedom'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
329
330
			// eliminate the file name from the path
331
			$filePath = dirname($share->getFileTarget()); // '/thedom.png' => '/' || '/Test/thedom.png' => '/Test'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
332
333
			// replace the first slash
334
			$pos = strpos($filePath, '/');
335
			if ($pos !== false) {
336
				$filePath = substr_replace($filePath, '', $pos, strlen('/')); // '/' => '' || '/Test/' => 'Test'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
337
			}
338
339 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
340
				// PDFs:
341
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.pdf');
342
			} else {
343
				// IMAGES:
344
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.txt');
345
			}
346
		} catch (Exception $e) {
347
			$this->handleException($e);
348
		}
349
	}
350
351
	/**
352
	 * Returns a not existing file name for PDF or image processing
353
	 * protected as of testing issues with static methods. (Actually
354
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
355
	 * @codeCoverageIgnore
356
	 *
357
	 * @param File $fileInfo
358
	 * @return string
359
	 */
360
	protected function buildTarget(File $fileInfo) {
361
		try {
362
			// get rid of the .png or .pdf and so on
363
			$fileName = substr($fileInfo->getName(), 0 , (strrpos($fileInfo->getName(), '.'))); // 'thedom.png' => 'thedom'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
364
365
			// eliminate the file name from the path
366
			$filePath = str_replace($fileInfo->getName(), '', $fileInfo->getPath()); // 'files/Test/thedom.png' => 'files/Test/' || 'files/thedom.png' => 'files/'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
367
368
			// and get the path on top of the files/ dir
369
			$filePath = str_replace('files', '', $filePath); // 'files/Test/' => '/Test/' || 'files/' => '/'
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
370
371
			// remove the last slash
372
			$filePath = substr_replace($filePath, '', strrpos($filePath, '/'), strlen('/')); // '/Test/' => '/Test' || '/' => ''
1 ignored issue
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
373
374
			// replace the first slash
375
			$pos = strpos($filePath, '/');
376
			if ($pos !== false) {
377
				$filePath = substr_replace($filePath, '', $pos, strlen('/')); // '/Test' => '// 'Test' || '/' => ''
378
			}
379
380 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
381
				// PDFs:
382
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.pdf');
383
			} else {
384
				// IMAGES:
385
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.txt');
386
			}
387
		} catch (Exception $e) {
388
			$this->handleException($e);
389
		}
390
	}
391
392
393
	/**
394
	 * Returns the fileInfo for each file in files and checks
395
	 * if it has a allowed MIME type and some other conditions.
396
	 *
397
	 * @param array $files
398
	 * @return File[]
399
	 * @throws NotFoundException
400
	 */
401 3
	private function buildFileInfo($files) {
402
		try {
403 3
			$fileArray = array();
404 3
			foreach ($files as $file) {
405
				// Check if anything is missing and file type is correct
406 3
				if (!empty($file['id'])) {
407
408 3
					$fileInfo = $this->fileMapper->find($file['id']);
409 3
					$this->checkMimeType($fileInfo);
410
411 2
					array_push($fileArray, $fileInfo);
412
				} else {
413 2
					throw new NotFoundException($this->l10n->t('Wrong parameter.'));
414
				}
415
			}
416 2
			return $fileArray;
417 1
		} catch (Exception $e) {
418 1
			$this->handleException($e);
419
		}
420
	}
421
422
	/**
423
	 * Checks a MIME type for a specifically given FileInfo.
424
	 * @param File $fileInfo
425
	 */
426 3
	private function checkMimeType(File $fileInfo) {
427
		try {
428 3
			if (!$fileInfo || !in_array($fileInfo->getMimetype(), $this::ALLOWED_MIMETYPES)) {
429 1
				$this->logger->debug('Getting FileInfo did not work or not included in the ALLOWED_MIMETYPES array.', ['app' => 'ocr']);
430 3
				throw new NotFoundException($this->l10n->t('Wrong MIME type.'));
431
			}
432 1
		} catch (Exception $e) {
433 1
			$this->handleException($e);
434
		}
435 2
	}
436
437
	/**
438
	 * @param File $fileInfo
439
	 * @return boolean|null
440
	 */
441 2
	private function checkSharedWithInitiator($fileInfo) {
442
		try {
443 2
			$owner = str_replace('home::', '', $fileInfo->getStoragename());
444 2
			if ($this->userId === $owner) {
445
				// user is owner (no shared file)
446 1
				return false;
447
			} else {
448
				// user is not owner (shared file)
449 1
				return true;
450
			}
451
		} catch (Exception $e) {
452
			$this->handleException($e);
453
		}
454
455
	}
456
457
	/**
458
	 * Finishes all Processed files by copying them to the right path and deleteing the temp files.
459
	 * Returns the number of processed files.
460
	 *
461
	 * @codeCoverageIgnore
462
	 * @return array
463
	 */
464
	private function handleProcessed() {
465
		try {
466
			$this->logger->debug('Check if files were processed by ocr and if so, put them to the right dirs.', ['app' => 'ocr']);
467
			$processed = $this->statusMapper->findAllProcessed($this->userId);
468
			foreach ($processed as $status) {
469
				if ($status->getType() === 'tess' && file_exists($status->getTempFile() . '.txt')) {
470
					//Save the tmp file with newname
471
					$this->view->file_put_contents($status->getTarget(), file_get_contents($status->getTempFile() . '.txt')); // need .txt because tesseract saves it like this
472
					// Cleaning temp files
473
					$this->statusMapper->delete($status);
474
					exec('rm ' . $status->getTempFile() . '.txt');
475
				} elseif ($status->getType() === 'mypdf' && file_exists($status->getTempFile())) {
476
					//Save the tmp file with newname
477
					$this->view->file_put_contents($status->getTarget(), file_get_contents($status->getTempFile())); // don't need to extend with .pdf / it uses the tmp file to save
478
					$this->statusMapper->delete($status);
479
					exec('rm ' . $status->getTempFile());
480
				} else {
481
					$status->setStatus('FAILED');
482
					$this->statusMapper->update($status);
483
					throw new NotFoundException($this->l10n->t('Temp file does not exist.'));
484
				}
485
			}
486
			return $processed;
487
		} catch (Exception $e) {
488
			$this->handleException($e);
489
		}
490
	}
491
492
	/**
493
	 * Removes ".txt" from the newName of a OCR status
494
	 *
495
	 * @param $status OcrStatus
496
	 * @return string
497
	 */
498 2
	private function removeFileExtension($status) {
499
		try {
500 2
			return substr($status->getTarget(), 0, strrpos($status->getTarget(), '_OCR'));
501
		} catch (Exception $e) {
502
			$this->handleException($e);
503
		}
504
	}
505
506
	/**
507
	 * Handles all failed orders of ocr processing queue and returns the status objects.
508
	 *
509
	 * @codeCoverageIgnore
510
	 * @return array
511
	 */
512
	private function handleFailed() {
513
		try {
514
			$failed = $this->statusMapper->findAllFailed($this->userId);
515
			foreach ($failed as $status) {
516
				// clean the tempfile
517
				exec('rm ' . $status->getTempFile());
518
				// set error displayed
519
				$status->setErrorDisplayed(true);
520
				$this->statusMapper->update($status);
521
			}
522
			$this->logger->debug('Following status objects failed: ' . json_encode($failed), ['app' => 'ocr']);
523
			return $failed;
524
		} catch (Exception $e) {
525
			$this->handleException($e);
526
		}
527
	}
528
529
	/**
530
	 * Handle the possible thrown Exceptions from all methods of this class.
531
	 *
532
	 * @param Exception $e
533
	 * @throws Exception
534
	 * @throws NotFoundException
535
	 */
536 5 View Code Duplication
	private function handleException($e) {
537 5
		$this->logger->logException($e, ['app' => 'ocr', 'message' => 'Exception during ocr service function processing']);
538 5
		if ($e instanceof NotFoundException) {
539 5
			throw new NotFoundException($e->getMessage());
540
		} else {
541
			throw $e;
542
		}
543
	}
544
}
545