Completed
Push — master ( 895bfc...f760e7 )
by Janis
03:47
created

OcrService::buildTarget()   B

Complexity

Conditions 4
Paths 16

Size

Total Lines 31
Code Lines 15

Duplication

Lines 7
Ratio 22.58 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 7
loc 31
ccs 0
cts 0
cp 0
rs 8.5806
cc 4
eloc 15
nc 16
nop 1
crap 20
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
70
	 */
71
	private $userId;
72
73
	/**
74
	 * @var IL10N
75
	 */
76
	private $l10n;
77
78
	/**
79
	 * Array of allowed mimetypes 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 mimetype for a pdf file
85
	 */
86
	const MIMETYPE_PDF = 'application/pdf';
87
88
	/**
89
	 * the only allowed image mimetypes 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 75
	public function __construct(ITempManager $tempManager, QueueService $queueService, OcrStatusMapper $mapper, FileMapper $fileMapper, ShareMapper $shareMapper, View $view, $userId, IL10N $l10n, ILogger $logger) {
105 75
		$this->logger = $logger;
106 75
		$this->tempM = $tempManager;
107 75
		$this->queueService = $queueService;
108 75
		$this->statusMapper = $mapper;
109 75
		$this->view = $view;
110 75
		$this->userId = $userId;
111 75
		$this->l10n = $l10n;
112 75
		$this->fileMapper = $fileMapper;
113 75
		$this->shareMapper = $shareMapper;
114 75
	}
115
116
	/**
117
	 * Gets the list of all available tesseract-ocr languages.
118
	 *
119
	 * @return string[] Languages
120
	 */
121 20
	public function listLanguages() {
122
		try {
123 20
			$success = -1;
124 20
			$this->logger->debug('Fetching languages. ', ['app' => 'ocr']);
125 20
			exec('tesseract --list-langs 2>&1', $result, $success);
126 20
			$this->logger->debug('Result for list-language command: ' . json_encode($result), ['app' => 'ocr']);
127 20
			if ($success === 0 && count($result) > 0) {
128 20
				if (is_array($result)) {
129 20
					$traineddata = $result;
130 4
				} else {
131
					throw new NotFoundException($this->l10n->t('No languages found.'));
132
				}
133 20
				$languages = array();
134 20
				array_shift($traineddata); // delete the first element of the array as it is a description of tesseract
135 20
				asort($traineddata); // sort the languages alphabetically
136 20
				foreach ($traineddata as $td) {
137 20
					$tdname = trim($td); // strip whitespaces
138 20
					array_push($languages, $tdname); //add to language list
139 4
				}
140 20
				$this->logger->debug('Fetched languages: ' . json_encode($languages), ['app' => 'ocr']);
141 20
				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 25
	public function process($languages, $files) {
159
		try {
160 25
			$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 25
			if (!empty($files) && !empty($languages) && count(array_diff($languages, $this->listLanguages())) === 0) {
163
164 15
				$fileInfo = $this->buildFileInfo($files);
165
166 10
				foreach ($fileInfo as $fInfo) {
167
					// Check if filelock existing
168
					// TODO: FileLock maybe \OC\Files\View::lockFile()
169
					// Check Shared
170 10
					$source = $fInfo->getPath();
171 10
					if ($this->checkSharedWithInitiator($fInfo)) {
172
						// Shared Item
173 5
						$source = str_replace('home::', '', $fInfo->getStoragename()) . '/' . $source;
174 5
						$target = $this->buildTargetForShared($fInfo);
175 1
					} else {
176
						// Not Shared
177 5
						$source = $this->userId . '/' . $source;
178 5
						$target = $this->buildTarget($fInfo);
179
					}
180
181
					// create a temp file for ocr processing purposes
182 10
					$tempFile = $this->tempM->getTemporaryFile();
183
184
					// set the running type
185 10
					if ($fInfo->getMimetype() === $this::MIMETYPE_PDF) {
186
						$ftype = 'mypdf';
187
					} else {
188 10
						$ftype = 'tess';
189
					}
190
					// Create status object
191 10
					$status = new OcrStatus('PENDING', $source, $target, $tempFile, $ftype, $this->userId, false);
192
					// Init client and send task / job
193
					// Feed the worker
194 10
					$this->queueService->clientSend($status, $languages, \OC::$SERVERROOT);
195 2
				}
196 10
				return 'PROCESSING';
197
			} else {
198 10
				throw new NotFoundException($this->l10n->t('Empty parameters passed.'));
199
			}
200 15
		} catch (Exception $e) {
201 15
			$this->handleException($e);
202
		}
203
	}
204
205
	/**
206
	 * A function which returns the JSONResponse for all required status checks and tasks.
207
	 * It will check for already processed, pending and failed ocr tasks and return them as needed.
208
	 *
209
	 * @codeCoverageIgnore
210
	 * @return string
211
	 */
212
	public function status() {
213
		try {
214
			// TODO: release lock
215
216
			// returns user specific processed files
217
			$processed = $this->handleProcessed();
218
219
			$pending = $this->statusMapper->findAllPending($this->userId);
220
221
			// return user specific failed state and set up error
222
			$failed = $this->handleFailed();
223
224
			return ['processed' => count($processed), 'failed' => count($failed), 'pending' => count($pending)];
225
		} catch (Exception $e) {
226
			$this->handleException($e);
227
		}
228
	}
229
230
	/**
231
	 * The command ocr:complete for occ will call this function in order to set the status.
232
	 * the worker should call it automatically after each processing step.
233
	 *
234
	 * @param integer $statusId
235
	 * @param boolean $failed
236
	 * @param string $errorMessage
237
	 */
238 15
	public function complete($statusId, $failed, $errorMessage) {
239
		try {
240 15
			$status = $this->statusMapper->find($statusId);
241 10
			if (!$failed) {
242 5
				$status->setStatus('PROCESSED');
243 5
				$this->statusMapper->update($status);
244 1
			} else {
245 5
				$status->setStatus('FAILED');
246 5
				$this->statusMapper->update($status);
247 8
				$this->logger->error($errorMessage, ['app' => 'ocr']);
248
			}
249 7
		} catch (Exception $e) {
250 5
			$this->handleException($e);
251
		}
252 10
	}
253
254
	/**
255
	 * The PersonalSettingsController will have the opportunity to delete ocr status objects.
256
	 *
257
	 * @param $statusId
258
	 * @param string $userId
259
	 * @return OcrStatus
260
	 */
261 10
	public function deleteStatus($statusId, $userId) {
262
		try {
263 10
			$status = $this->statusMapper->find($statusId);
264 5
			if ($status->getUserId() !== $userId) {
265
				throw new NotFoundException($this->l10n->t('Cannot delete. Wrong owner.'));
266
			} else {
267 5
				$status = $this->statusMapper->delete($status);
268
			}
269 5
			$status->setTarget($this->removeFileExtension($status));
270 5
			$status->setSource(null);
271 5
			$status->setTempFile(null);
272 5
			$status->setType(null);
273 5
			$status->setErrorDisplayed(null);
274 5
			return $status;
275 5
		} catch (Exception $e) {
276 5
			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...
277 5
				$ex = new NotFoundException($this->l10n->t('Cannot delete. Wrong id.'));
278 5
				$this->handleException($ex);
279
			} else {
280
				$this->handleException($e);
281
			}
282
		}
283
	}
284
285
	/**
286
	 * Gets all status objects for a specific user in order to list them on the personal settings page.
287
	 *
288
	 * @param string $userId
289
	 * @return array
290
	 */
291 5
	public function getAllForPersonal($userId) {
292
		try {
293 5
			$status = $this->statusMapper->findAll($userId);
294 5
			$statusNew = array();
295 5
			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...
296 5
				$newName = $this->removeFileExtension($status[$x]);
297 5
				$status[$x]->setTarget($newName);
298 5
				$status[$x]->setSource(null);
299 5
				$status[$x]->setTempFile(null);
300 5
				$status[$x]->setType(null);
301 5
				$status[$x]->setErrorDisplayed(null);
302 5
				array_push($statusNew, $status[$x]);
303 1
			}
304 5
			return $statusNew;
305
		} catch (Exception $e) {
306
			$this->handleException($e);
307
		}
308
	}
309
310
	/**
311
	 * Returns a not existing file name for pdf or image processing
312
	 * protected as of testing issues with static methods. (Actually
313
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
314
	 * @codeCoverageIgnore
315
	 *
316
	 * @param File $fileInfo
317
	 * @return string
318
	 */
319
	protected function buildTargetForShared(File $fileInfo) {
320
		try {
321
			$share = $this->shareMapper->find($fileInfo->getFileid(), $this->userId, str_replace('home::', '', $fileInfo->getStoragename()));
322
323
			// get rid of the .png or .pdf and so on
324
			$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...
325
326
			// remove everything in front of and including of the first appearance of a slash from behind
327
			$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...
328
329
			// eliminate the file name from the path
330
			$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...
331
332
			// replace the first slash
333
			$pos = strpos($filePath, '/');
334
			if ($pos !== false) {
335
				$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...
336
			}
337
338 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
339
				// PDFs:
340
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.pdf');
341
			} else {
342
				// IMAGES:
343
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.txt');
344
			}
345
		} catch (Exception $e) {
346
			$this->handleException($e);
347
		}
348
	}
349
350
	/**
351
	 * Returns a not existing file name for pdf or image processing
352
	 * protected as of testing issues with static methods. (Actually
353
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
354
	 * @codeCoverageIgnore
355
	 *
356
	 * @param File $fileInfo
357
	 * @return string
358
	 */
359
	protected function buildTarget(File $fileInfo) {
360
		try {
361
			// get rid of the .png or .pdf and so on
362
			$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...
363
364
			// eliminate the file name from the path
365
			$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...
366
367
			// and get the path on top of the files/ dir
368
			$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...
369
370
			// remove the last slash
371
			$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...
372
373
			// replace the first slash
374
			$pos = strpos($filePath, '/');
375
			if ($pos !== false) {
376
				$filePath = substr_replace($filePath, '', $pos, strlen('/')); // '/Test' => '// 'Test' || '/' => ''
377
			}
378
379 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
380
				// PDFs:
381
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.pdf');
382
			} else {
383
				// IMAGES:
384
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.txt');
385
			}
386
		} catch (Exception $e) {
387
			$this->handleException($e);
388
		}
389
	}
390
391
392
	/**
393
	 * Returns the fileInfo for each file in files and checks
394
	 * if it has a allowed mimetype and some other conditions.
395
	 *
396
	 * @param array $files
397
	 * @return File[]
398
	 * @throws NotFoundException
399
	 */
400 15
	private function buildFileInfo($files) {
401
		try {
402 15
			$fileArray = array();
403 15
			foreach ($files as $file) {
404
				// Check if anything is missing and file type is correct
405 15
				if (!empty($file['id'])) {
406
407 15
					$fileInfo = $this->fileMapper->find($file['id']);
408 15
					$this->checkMimeType($fileInfo);
409
410 10
					array_push($fileArray, $fileInfo);
411 2
				} else {
412 6
					throw new NotFoundException($this->l10n->t('Wrong parameter.'));
413
				}
414 2
			}
415 10
			return $fileArray;
416 5
		} catch (Exception $e) {
417 5
			$this->handleException($e);
418
		}
419
	}
420
421
	/**
422
	 * Checks a Mimetype for a specific given FileInfo.
423
	 * @param File $fileInfo
424
	 */
425 15
	private function checkMimeType(File $fileInfo) {
426
		try {
427 15
			if (!$fileInfo || !in_array($fileInfo->getMimetype(), $this::ALLOWED_MIMETYPES)) {
428 5
				$this->logger->debug('Getting FileInfo did not work or not included in the ALLOWED_MIMETYPES array.', ['app' => 'ocr']);
429 11
				throw new NotFoundException($this->l10n->t('Wrong mimetype.'));
430
			}
431 7
		} catch (Exception $e) {
432 5
			$this->handleException($e);
433
		}
434 10
	}
435
436
	/**
437
	 * @param File $fileInfo
438
	 * @return boolean|null
439
	 */
440 10
	private function checkSharedWithInitiator($fileInfo) {
441
		try {
442 10
			$owner = str_replace('home::', '', $fileInfo->getStoragename());
443 10
			if ($this->userId === $owner) {
444
				// user is owner (no shared file)
445 5
				return false;
446
			} else {
447
				// user is not owner (shared file)
448 5
				return true;
449
			}
450
		} catch (Exception $e) {
451
			$this->handleException($e);
452
		}
453
454
	}
455
456
	/**
457
	 * Finishes all Processed files by copying them to the right path and deleteing the temp files.
458
	 * Returns the number of processed files.
459
	 *
460
	 * @codeCoverageIgnore
461
	 * @return array
462
	 */
463
	private function handleProcessed() {
464
		try {
465
			$this->logger->debug('Check if files were processed by ocr and if so, put them to the right dirs.', ['app' => 'ocr']);
466
			$processed = $this->statusMapper->findAllProcessed($this->userId);
467
			foreach ($processed as $status) {
468
				if ($status->getType() === 'tess' && file_exists($status->getTempFile() . '.txt')) {
469
					//Save the tmp file with newname
470
					$this->view->file_put_contents($status->getTarget(), file_get_contents($status->getTempFile() . '.txt')); // need .txt because tesseract saves it like this
471
					// Cleaning temp files
472
					$this->statusMapper->delete($status);
473
					exec('rm ' . $status->getTempFile() . '.txt');
474
				} elseif ($status->getType() === 'mypdf' && file_exists($status->getTempFile())) {
475
					//Save the tmp file with newname
476
					$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
477
					$this->statusMapper->delete($status);
478
					exec('rm ' . $status->getTempFile());
479
				} else {
480
					$status->setStatus('FAILED');
481
					$this->statusMapper->update($status);
482
					throw new NotFoundException($this->l10n->t('Temp file does not exist.'));
483
				}
484
			}
485
			return $processed;
486
		} catch (Exception $e) {
487
			$this->handleException($e);
488
		}
489
	}
490
491
	/**
492
	 * Removes ".txt" from the newName of a ocr status
493
	 *
494
	 * @param $status OcrStatus
495
	 * @return string
496
	 */
497 10
	private function removeFileExtension($status) {
498
		try {
499 10
			return substr($status->getTarget(), 0, strrpos($status->getTarget(), '_OCR'));
500
		} catch (Exception $e) {
501
			$this->handleException($e);
502
		}
503
	}
504
505
	/**
506
	 * Handles all failed orders of ocr processing queue and returns the status objects.
507
	 *
508
	 * @codeCoverageIgnore
509
	 * @return array
510
	 */
511
	private function handleFailed() {
512
		try {
513
			$failed = $this->statusMapper->findAllFailed($this->userId);
514
			foreach ($failed as $status) {
515
				// clean the tempfile
516
				exec('rm ' . $status->getTempFile());
517
				// set error displayed
518
				$status->setErrorDisplayed(true);
519
				$this->statusMapper->update($status);
520
			}
521
			$this->logger->debug('Following status objects failed: ' . json_encode($failed), ['app' => 'ocr']);
522
			return $failed;
523
		} catch (Exception $e) {
524
			$this->handleException($e);
525
		}
526
	}
527
528
	/**
529
	 * Handle the possible thrown Exceptions from all methods of this class.
530
	 *
531
	 * @param Exception $e
532
	 * @throws Exception
533
	 * @throws NotFoundException
534
	 */
535 25 View Code Duplication
	private function handleException($e) {
536 25
		$this->logger->logException($e, ['app' => 'ocr', 'message' => 'Exception during ocr service function processing']);
537 25
		if ($e instanceof NotFoundException) {
538 25
			throw new NotFoundException($e->getMessage());
539
		} else {
540
			throw $e;
541
		}
542
	}
543
}
544