Completed
Push — master ( 54efaa...464bdd )
by Janis
04:10
created

OcrService   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 510
Duplicated Lines 4.31 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 83.22%

Importance

Changes 0
Metric Value
wmc 60
lcom 1
cbo 8
dl 22
loc 510
ccs 119
cts 143
cp 0.8322
rs 6.0975
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
B listLanguages() 0 27 6
C process() 0 46 8
A status() 0 17 2
A complete() 0 15 3
B deleteStatus() 0 23 4
A getAllForPersonal() 0 18 3
B buildTargetForShared() 7 30 4
B buildTarget() 7 31 4
A buildFileInfo() 0 20 4
A checkMimeType() 0 10 4
A checkSharedWithInitiator() 0 15 3
C handleProcessed() 0 25 7
A removeFileExtension() 0 7 2
A handleFailed() 0 16 3
A handleException() 8 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like OcrService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OcrService, and based on these observations, apply Extract Interface, too.

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'];
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'];
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
	 *
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
			if ($success === 0 && count($result) > 0) {
127 4
				if (is_array($result)) {
128 4
					$traineddata = $result;
129 4
				} else {
130
					throw new NotFoundException($this->l10n->t('No languages found.'));
131
				}
132 4
				$languages = array();
133 4
				array_shift($traineddata); // delete the first element of the array as it is a description of tesseract
134 4
				asort($traineddata); // sort the languages alphabetically
135 4
				foreach ($traineddata as $td) {
136 4
					$tdname = trim($td); // strip whitespaces
137 4
					array_push($languages, $tdname); //add to language list
138 4
				}
139 4
				$this->logger->debug('Fetched languages: ' . json_encode($languages), ['app' => 'ocr']);
140 4
				return $languages;
141
			} else {
142
				throw new NotFoundException($this->l10n->t('No languages found.'));
143
			}
144
		} catch (Exception $e) {
145
			$this->handleException($e);
146
		}
147
	}
148
149
	/**
150
	 * Processes and prepares the files for ocr.
151
	 * Sends the stuff to the client in order to ocr async.
152
	 *
153
	 * @param string[] $languages
154
	 * @param array $files
155
	 * @return string
156
	 */
157 5
	public function process($languages, $files) {
158
		try {
159 5
			$this->logger->debug('Will now process files: ' . json_encode($files) . ' with languages: ' . json_encode($languages), ['app' => 'ocr']);
160
			// Check if files and language not empty
161 5
			if (!empty($files) && !empty($languages) && count(array_diff($languages, $this->listLanguages())) === 0) {
162
163 3
				$fileInfo = $this->buildFileInfo($files);
164
165 2
				foreach ($fileInfo as $fInfo) {
166
					// Check if filelock existing
167
					// TODO: FileLock maybe \OC\Files\View::lockFile()
168
					// Check Shared
169 2
					$source = $fInfo->getPath();
170 2
					if ($this->checkSharedWithInitiator($fInfo)) {
171
						// Shared Item
172 1
						$source = str_replace('home::', '', $fInfo->getStoragename()) . '/' . $source;
173 1
						$target = $this->buildTargetForShared($fInfo);
174 1
					} else {
175
						// Not Shared
176 1
						$source = $this->userId . '/' . $source;
177 1
						$target = $this->buildTarget($fInfo);
178
					}
179
180
					// create a temp file for ocr processing purposes
181 2
					$tempFile = $this->tempM->getTemporaryFile();
182
183
					// set the running type
184 2
					if ($fInfo->getMimetype() === $this::MIMETYPE_PDF) {
185
						$ftype = 'mypdf';
186
					} else {
187 2
						$ftype = 'tess';
188
					}
189
					// Create status object
190 2
					$status = new OcrStatus('PENDING', $source, $target, $tempFile, $ftype, $this->userId, false);
191
					// Init client and send task / job
192
					// Feed the worker
193 2
					$this->queueService->clientSend($status, $languages, \OC::$SERVERROOT);
194 2
				}
195 2
				return 'PROCESSING';
196
			} else {
197 2
				throw new NotFoundException($this->l10n->t('Empty parameters passed.'));
198
			}
199 3
		} catch (Exception $e) {
200 3
			$this->handleException($e);
201
		}
202
	}
203
204
	/**
205
	 * A function which returns the JSONResponse for all required status checks and tasks.
206
	 * It will check for already processed, pending and failed ocr tasks and return them as needed.
207
	 *
208
	 * @codeCoverageIgnore
209
	 * @return string
210
	 */
211
	public function status() {
212
		try {
213
			// TODO: release lock
214
215
			// returns user specific processed files
216
			$processed = $this->handleProcessed();
217
218
			$pending = $this->statusMapper->findAllPending($this->userId);
219
220
			// return user specific failed state and set up error
221
			$failed = $this->handleFailed();
222
223
			return ['processed' => count($processed), 'failed' => count($failed), 'pending' => count($pending)];
224
		} catch (Exception $e) {
225
			$this->handleException($e);
226
		}
227
	}
228
229
	/**
230
	 * The command ocr:complete for occ will call this function in order to set the status.
231
	 * the worker should call it automatically after each processing step.
232
	 *
233
	 * @param integer $statusId
234
	 * @param boolean $failed
235
	 * @param string $errorMessage
236
	 */
237 3
	public function complete($statusId, $failed, $errorMessage) {
238
		try {
239 3
			$status = $this->statusMapper->find($statusId);
240 2
			if (!$failed) {
241 1
				$status->setStatus('PROCESSED');
242 1
				$this->statusMapper->update($status);
243 1
			} else {
244 1
				$status->setStatus('FAILED');
245 1
				$this->statusMapper->update($status);
246 1
				$this->logger->error($errorMessage, ['app' => 'ocr']);
247
			}
248 3
		} catch (Exception $e) {
249 1
			$this->handleException($e);
250
		}
251 2
	}
252
253
	/**
254
	 * The PersonalSettingsController will have the opportunity to delete ocr status objects.
255
	 *
256
	 * @param $statusId
257
	 * @param string $userId
258
	 * @return OcrStatus
259
	 */
260 2
	public function deleteStatus($statusId, $userId) {
261
		try {
262 2
			$status = $this->statusMapper->find($statusId);
263 1
			if ($status->getUserId() !== $userId) {
264
				throw new NotFoundException($this->l10n->t('Cannot delete. Wrong owner.'));
265
			} else {
266 1
				$status = $this->statusMapper->delete($status);
267
			}
268 1
			$status->setTarget($this->removeFileExtension($status));
269 1
			$status->setSource(null);
270 1
			$status->setTempFile(null);
271 1
			$status->setType(null);
272 1
			$status->setErrorDisplayed(null);
273 1
			return $status;
274 1
		} catch (Exception $e) {
275 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...
276 1
				$ex = new NotFoundException($this->l10n->t('Cannot delete. Wrong id.'));
277 1
				$this->handleException($ex);
278
			} else {
279
				$this->handleException($e);
280
			}
281
		}
282
	}
283
284
	/**
285
	 * Gets all status objects for a specific user in order to list them on the personal settings page.
286
	 *
287
	 * @param string $userId
288
	 * @return array
289
	 */
290 1
	public function getAllForPersonal($userId) {
291
		try {
292 1
			$status = $this->statusMapper->findAll($userId);
293 1
			$statusNew = array();
294 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...
295 1
				$newName = $this->removeFileExtension($status[$x]);
296 1
				$status[$x]->setTarget($newName);
297 1
				$status[$x]->setSource(null);
298 1
				$status[$x]->setTempFile(null);
299 1
				$status[$x]->setType(null);
300 1
				$status[$x]->setErrorDisplayed(null);
301 1
				array_push($statusNew, $status[$x]);
302 1
			}
303 1
			return $statusNew;
304
		} catch (Exception $e) {
305
			$this->handleException($e);
306
		}
307
	}
308
309
	/**
310
	 * Returns a not existing file name for pdf or image processing
311
	 * protected as of testing issues with static methods. (Actually
312
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
313
	 * @codeCoverageIgnore
314
	 *
315
	 * @param File $fileInfo
316
	 * @return string
317
	 */
318
	protected function buildTargetForShared(File $fileInfo) {
319
		try {
320
			$share = $this->shareMapper->find($fileInfo->getFileid(), $this->userId, str_replace('home::', '', $fileInfo->getStoragename()));
321
322
			// get rid of the .png or .pdf and so on
323
			$fileName = substr($share->getFileTarget(), 0, -4); // '/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...
324
325
			// remove everything in front of and including of the first appearance of a slash from behind
326
			$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...
327
328
			// eliminate the file name from the path
329
			$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...
330
331
			// replace the first slash
332
			$pos = strpos($filePath, '/');
333
			if ($pos !== false) {
334
				$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...
335
			}
336
337 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
338
				// PDFs:
339
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.pdf');
340
			} else {
341
				// IMAGES:
342
				return \OCP\Files::buildNotExistingFileName($filePath, $fileName . '_OCR.txt');
343
			}
344
		} catch (Exception $e) {
345
			$this->handleException($e);
346
		}
347
	}
348
349
	/**
350
	 * Returns a not existing file name for pdf or image processing
351
	 * protected as of testing issues with static methods. (Actually
352
	 * it will be mocked partially) FIXME: Change this behaviour as soon as the buidlNotExistingFileName function is not static anymore
353
	 * @codeCoverageIgnore
354
	 *
355
	 * @param File $fileInfo
356
	 * @return string
357
	 */
358
	protected function buildTarget(File $fileInfo) {
359
		try {
360
			// get rid of the .png or .pdf and so on
361
			$fileName = substr($fileInfo->getName(), 0, -4); // '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...
362
363
			// eliminate the file name from the path
364
			$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...
365
366
			// and get the path on top of the files/ dir
367
			$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...
368
369
			// remove the last slash
370
			$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...
371
372
			// replace the first slash
373
			$pos = strpos($filePath, '/');
374
			if ($pos !== false) {
375
				$filePath = substr_replace($filePath, '', $pos, strlen('/')); // '/Test' => '// 'Test' || '/' => ''
376
			}
377
378 View Code Duplication
			if ($fileInfo->getMimetype() === $this::MIMETYPE_PDF) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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