Completed
Pull Request — master (#93)
by Janis
14:35
created

JobService::handleException()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 11
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 11
loc 11
rs 9.4285
c 1
b 0
f 0
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
3
/**
4
 * Nextcloud - OCR
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Janis Koehr <[email protected]>
10
 * @copyright Janis Koehr 2017
11
 */
12
namespace OCA\Ocr\Service;
13
14
use OCA\Ocr\Db\OcrJobMapper;
15
use OC\Files\View;
16
use OCP\ILogger;
17
use OCP\IL10N;
18
use OCP\ITempManager;
19
use OCA\Ocr\Db\OcrJob;
20
use OCA\Ocr\Db\File;
21
22
/**
23
 * Class JobService
24
 *
25
 * @package OCA\Ocr\Service
26
 */
27
class JobService {
28
	
29
	/**
30
	 *
31
	 * @var ILogger
32
	 */
33
	private $logger;
34
	
35
	/**
36
	 *
37
	 * @var QueueService
38
	 */
39
	private $queueService;
40
	
41
	/**
42
	 *
43
	 * @var OcrJobMapper
44
	 */
45
	private $jobMapper;
46
	
47
	/**
48
	 *
49
	 * @var View
50
	 */
51
	private $view;
52
	
53
	/**
54
	 *
55
	 * @var String
56
	 */
57
	private $userId;
58
	
59
	/**
60
	 *
61
	 * @var IL10N
62
	 */
63
	private $l10n;
64
	
65
	/**
66
	 *
67
	 * @var FileService
68
	 */
69
	private $fileService;
70
	
71
	/**
72
	 *
73
	 * @var ITempManager
74
	 */
75
	private $tempM;
76
	
77
	/**
78
	 * JobService constructor.
79
	 *
80
	 * @param IL10N $l10n        	
81
	 * @param ILogger $logger        	
82
	 * @param string $userId        	
83
	 * @param View $view        	
84
	 * @param QueueService $queueService        	
85
	 * @param OcrJobMapper $mapper        	
86
	 * @param FileService $fileService        	
87
	 */
88
	public function __construct(IL10N $l10n, ILogger $logger, $userId, View $view, ITempManager $tempManager, QueueService $queueService, OcrJobMapper $mapper, FileService $fileService) {
89
		$this->logger = $logger;
90
		$this->queueService = $queueService;
91
		$this->jobMapper = $mapper;
92
		$this->view = $view;
93
		$this->userId = $userId;
94
		$this->l10n = $l10n;
95
		$this->fileService = $fileService;
96
		$this->tempM = $tempManager;
97
	}
98
	
99
	/**
100
	 * Processes and prepares the files for OCR.
101
	 * Sends the stuff to the client in order to OCR async.
102
	 *
103
	 * @param string[] $languages        	
104
	 * @param array $files        	
105
	 * @return string
106
	 */
107
	public function process($languages, $files) {
108
		try {
109
			$this->logger->debug ( 'Will now process files: ' . json_encode ( $files ) . ' with languages: ' . json_encode ( $languages ), [ 
110
					'app' => 'ocr' 
111
			] );
112
			// Check if files and language not empty
113
			if (! empty ( $files ) && $this->checkForAcceptedLanguages ( $languages )) {
114
				$fileInfo = $this->fileService->buildFileInfo ( $files );
115
				foreach ( $fileInfo as $fInfo ) {
0 ignored issues
show
Bug introduced by
The expression $fileInfo of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
116
					// Check Shared
117
					$shared = $this->fileService->checkSharedWithInitiator ( $fInfo );
118
					$target = $this->fileService->buildTarget ( $fInfo, $shared );
0 ignored issues
show
Bug introduced by
It seems like $shared defined by $this->fileService->chec...edWithInitiator($fInfo) on line 117 can also be of type null; however, OCA\Ocr\Service\FileService::buildTarget() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
119
					$source = $this->fileService->buildSource ( $fInfo, $shared );
0 ignored issues
show
Bug introduced by
It seems like $shared defined by $this->fileService->chec...edWithInitiator($fInfo) on line 117 can also be of type null; however, OCA\Ocr\Service\FileService::buildSource() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
120
					
121
					// create a temp file for ocr processing purposes
122
					$tempFile = $this->tempM->getTemporaryFile ();
123
					
124
					// set the running type
125
					$fType = $this->fileService->getCorrectType ();
0 ignored issues
show
Bug introduced by
The call to getCorrectType() misses a required argument $fileInfo.

This check looks for function calls that miss required arguments.

Loading history...
126
					
127
					// TODO: create a security token
128
					// Create job object
129
					$job = new OcrJob ( 'PENDING', $source, $target, $tempFile, $fType, $this->userId, false );
130
					
131
					// Init client and send task / job
132
					// Feed the worker
133
					$this->queueService->sendJob ( $job, $languages, \OC::$SERVERROOT );
134
				}
135
				return 'PROCESSING';
136
			} else {
137
				throw new NotFoundException ( $this->l10n->t ( 'Empty parameters passed.' ) );
138
			}
139
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
140
			$this->handleException ( $e );
141
		}
142
	}
143
	
144
	/**
145
	 * Delete an ocr job for a given id and userId.
146
	 *
147
	 * @param
148
	 *        	$jobId
149
	 * @param string $userId        	
150
	 * @return OcrJob
151
	 */
152
	public function deleteJob($jobId, $userId) {
153
		try {
154
			$job = $this->jobMapper->find ( $jobId );
155
			if ($job->getUserId () !== $userId) {
156
				throw new NotFoundException ( $this->l10n->t ( 'Cannot delete. Wrong owner.' ) );
157
			} else {
158
				$job = $this->jobMapper->delete ( $job );
159
			}
160
			// TODO: return sanitized job for the personal settings page (no information about the source and target and such things.
161
			return $job;
162
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
163
			if ($e instanceof DoesNotExistException) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\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...
164
				$ex = new NotFoundException ( $this->l10n->t ( 'Cannot delete. Wrong ID.' ) );
165
				$this->handleException ( $ex );
0 ignored issues
show
Documentation introduced by
$ex is of type object<OCA\Ocr\Service\NotFoundException>, but the function expects a object<OCA\Ocr\Service\Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
166
			} else {
167
				$this->handleException ( $e );
168
			}
169
		}
170
	}
171
	
172
	/**
173
	 * Gets all job objects for a specific user.
174
	 *
175
	 * @param string $userId        	
176
	 * @return array
177
	 */
178
	public function getAllJobsForUser($userId) {
179
		try {
180
			$jobs = $this->jobMapper->findAll ( $userId );
181
			$jobsNew = array ();
182
			for($x = 0; $x < count ( $jobs ); $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...
183
				$newName = $this->removeFileExtension ( $jobs [$x] );
0 ignored issues
show
Bug introduced by
The method removeFileExtension() does not seem to exist on object<OCA\Ocr\Service\JobService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
184
				$jobs [$x]->setTarget ( $newName );
185
				$jobs [$x]->setSource ( null );
186
				$jobs [$x]->setTempFile ( null );
187
				$jobs [$x]->setType ( null );
188
				$jobs [$x]->setErrorDisplayed ( null );
189
				array_push ( $jobsNew, $jobs [$x] );
190
			}
191
			return $jobsNew;
192
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
193
			$this->handleException ( $e );
194
		}
195
	}
196
	
197
	/**
198
	 * TODO: Verify the security token and so on.
199
	 * The function the worker will call in order to set the jobs status.
200
	 * The worker should call it automatically after each processing step.
201
	 *
202
	 * @param integer $jobId        	
203
	 * @param boolean $failed        	
204
	 * @param string $errorMessage        	
205
	 */
206
	public function jobFinished($jobId, $failed, $errorMessage) {
207
		try {
208
			$job = $this->jobMapper->find ( $jobId );
209
			if (! $failed) {
210
				$job->setStatus ( 'PROCESSED' );
211
				$this->jobMapper->update ( $job );
212
			} else {
213
				$job->setStatus ( 'FAILED' );
214
				// TODO: add error message log and so on
215
				$this->jobMapper->update ( $job );
216
				$this->logger->error ( $errorMessage, [ 
217
						'app' => 'ocr' 
218
				] );
219
			}
220
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
221
			$this->handleException ( $e );
222
		}
223
	}
224
	
225
	/**
226
	 * Finishes all Processed files by copying them to the right path and deleteing the temp files.
227
	 * Returns the number of processed files.
228
	 * TODO: adjust behaviour for saving of the worker with the right file extension or not
229
	 * 
230
	 * @return array
231
	 */
232
	public function handleProcessed() {
233
		try {
234
			$this->logger->debug ( 'Check if files were processed by ocr and if so, put them to the right dirs.', [ 
235
					'app' => 'ocr' 
236
			] );
237
			$processed = $this->jobMapper->findAllProcessed ( $this->userId );
238
			foreach ( $processed as $job ) {
239
				if ($job->getType () === 'tess' && $this->fileService->fileExists ( $job->getTempFile () . '.txt' )) {
240
					// Save the tmp file with newname
241
					$this->view->file_put_contents ( $job->getTarget (), $this->fileService->getFileContents ( $job->getTempFile () . '.txt' ) ); // need .txt because tesseract saves it like this
242
					                                                                                                                      // Cleaning temp files
243
					$this->jobMapper->delete ( $job );
244
					$this->fileService->execRemove ( $job->getTempFile () . '.txt' );
245
				} elseif ($job->getType () === 'mypdf' && $this->fileService->fileExists ( $job->getTempFile () )) {
246
					// Save the tmp file with newname
247
					$this->view->file_put_contents ( $job->getTarget (), $this->fileService->getFileContents ( $job->getTempFile () ) ); // don't need to extend with .pdf / it uses the tmp file to save
248
					$this->jobMapper->delete ( $job );
249
					$this->fileService->execRemove ( $job->getTempFile () );
250
				} else {
251
					$job->setStatus ( 'FAILED' );
252
					$this->jobMapper->update ( $job );
253
					throw new NotFoundException ( $this->l10n->t ( 'Temp file does not exist.' ) );
254
				}
255
			}
256
			return $processed;
257
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
258
			$this->handleException ( $e );
259
		}
260
	}
261
	
262
	/**
263
	 * Handles all failed orders of ocr processing queue and returns the job objects.
264
	 *
265
	 * @return array
266
	 */
267
	public function handleFailed() {
268
		try {
269
			$failed = $this->jobMapper->findAllFailed ( $this->userId );
270
			foreach ( $failed as $job ) {
271
				// clean the tempfile
272
				$this->fileService->execRemove ( $job->getTempFile () );
273
				// set error displayed
274
				$job->setErrorDisplayed ( true );
275
				$this->jobMapper->update ( $job );
276
			}
277
			$this->logger->debug ( 'Following jobs failed: ' . json_encode ( $failed ), [ 
278
					'app' => 'ocr' 
279
			] );
280
			return $failed;
281
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The class OCA\Ocr\Service\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
282
			$this->handleException ( $e );
283
		}
284
	}
285
	
286
	/**
287
	 * Checks if the given languages are supported or not.
288
	 * TODO: $this->config->getAppValue()
289
	 *
290
	 * @param string[] $languages        	
291
	 * @return boolean
292
	 */
293
	private function checkForAcceptedLanguages($languages) {
294
		if (empty ( $languages )) {
295
			return true;
296
		} elseif (count ( array_diff ( $languages, [ 
297
				'deu',
298
				'eng' 
299
		] ) ) === 0) {
300
			return true;
301
		} else {
302
			return false;
303
		}
304
	}
305
	
306
	/**
307
	 * Handle the possible thrown Exceptions from all methods of this class.
308
	 *
309
	 * @param Exception $e        	
310
	 * @throws Exception
311
	 * @throws NotFoundException
312
	 */
313 View Code Duplication
	private function handleException($e) {
314
		$this->logger->logException ( $e, [ 
315
				'app' => 'ocr',
316
				'message' => 'Exception during job service function processing' 
317
		] );
318
		if ($e instanceof NotFoundException) {
319
			throw new NotFoundException ( $e->getMessage () );
320
		} else {
321
			throw $e;
322
		}
323
	}
324
}