Passed
Push — install-models-command ( 24a3b7...46ff74 )
by Matias
04:25
created

SetupCommand::getModelFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2019, Matias De lellis <[email protected]>
4
 *
5
 * @author Matias De lellis <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
namespace OCA\FaceRecognition\Command;
24
25
use OCP\ITempManager;
26
27
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Command\Command was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use Symfony\Component\Console\Input\InputOption;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputOption was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use Symfony\Component\Console\Input\InputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use Symfony\Component\Console\Output\OutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Output\OutputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
32
use OCA\FaceRecognition\Service\ModelService;
0 ignored issues
show
Bug introduced by
The type OCA\FaceRecognition\Service\ModelService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
34
class SetupCommand extends Command {
35
36
	/** @var ModelService */
37
	protected $modelService;
38
39
	/** @var ITempManager */
40
	protected $tempManager;
41
42
	/** @var OutputInterface */
43
	protected $logger;
44
45
	/*
46
	 * Model 1
47
	 */
48
	private $detectorModelUrl = 'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2';
49
	private $detectorModel = 'mmod_human_face_detector.dat';
50
51
	private $resnetModelUrl = 'https://github.com/davisking/dlib-models/raw/2a61575dd45d818271c085ff8cd747613a48f20d/dlib_face_recognition_resnet_model_v1.dat.bz2';
52
	private $resnetModel = 'dlib_face_recognition_resnet_model_v1.dat';
53
54
	private $predictorModelUrl = 'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_5_face_landmarks.dat.bz2';
55
	private $predictorModel = 'shape_predictor_5_face_landmarks.dat';
56
57
	/**
58
	 * @param ModelService $modelService
59
	 * @param ITempManager $tempManager
60
	 */
61
	public function __construct(ModelService $modelService,
62
	                            ITempManager $tempManager) {
63
		parent::__construct();
64
65
		$this->modelService = $modelService;
66
		$this->tempManager  = $tempManager;
67
	}
68
69
	protected function configure() {
70
		$this
71
			->setName('face:setup')
72
			->setDescription('Download and Setup the model 1 used for the analysis');
73
	}
74
75
	/**
76
	 * @param InputInterface $input
77
	 * @param OutputInterface $output
78
	 * @return int
79
	 */
80
	protected function execute(InputInterface $input, OutputInterface $output) {
81
82
		$this->logger = $output;
83
84
		$this->modelService->useModelVersion(1);
85
		$this->tempFolder = $this->tempManager->getTemporaryFolder('/facerecognition/');
0 ignored issues
show
Bug Best Practice introduced by
The property tempFolder does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
86
87
		$this->downloadModel ($this->detectorModelUrl);
88
		$this->bunzip2 ($this->getDownloadedFile($this->detectorModelUrl), $this->modelService->getModelFile($this->detectorModel));
89
90
		$this->downloadModel ($this->resnetModelUrl);
91
		$this->bunzip2 ($this->getDownloadedFile($this->resnetModelUrl), $this->modelService->getModelFile($this->resnetModel));
92
93
		$this->downloadModel ($this->predictorModelUrl);
94
		$this->bunzip2 ($this->getDownloadedFile($this->predictorModelUrl), $this->modelService->getModelFile($this->predictorModel));
95
96
		$this->logger->writeln('Install models successfully done');
97
98
		$this->tempManager->clean();
99
100
		return 0;
101
	}
102
103
	/**
104
	 * Downloads the facereconition model to an temp folder.
105
	 *
106
	 * @throws \Exception
107
	 */
108
	private function downloadModel(string $url) {
109
		$this->logger->writeln('Downloading: ' . $url . ' on ' . $this->getDownloadedFile($url));
110
111
		$fp = fopen($this->getDownloadedFile($url), 'w+');
112
		$ch = curl_init($url);
113
		curl_setopt_array($ch, [
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt_array() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
		curl_setopt_array(/** @scrutinizer ignore-type */ $ch, [
Loading history...
114
			CURLOPT_FILE => $fp,
115
			CURLOPT_FOLLOWLOCATION => true,
116
			CURLOPT_SSL_VERIFYPEER => false,
117
			CURLOPT_SSL_VERIFYHOST => 0,
118
			CURLOPT_USERAGENT => 'Nextcloud Facerecognition Installer',
119
		]);
120
121
		if(curl_exec($ch) === false) {
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
		if(curl_exec(/** @scrutinizer ignore-type */ $ch) === false) {
Loading history...
122
			throw new \Exception('Curl error: ' . curl_error($ch));
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_error() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
			throw new \Exception('Curl error: ' . curl_error(/** @scrutinizer ignore-type */ $ch));
Loading history...
123
		}
124
125
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
		$httpCode = curl_getinfo(/** @scrutinizer ignore-type */ $ch, CURLINFO_HTTP_CODE);
Loading history...
126
		if ($httpCode !== 200) {
127
			$statusCodes = [
128
				400 => 'Bad request',
129
				401 => 'Unauthorized',
130
				403 => 'Forbidden',
131
				404 => 'Not Found',
132
				500 => 'Internal Server Error',
133
				502 => 'Bad Gateway',
134
				503 => 'Service Unavailable',
135
				504 => 'Gateway Timeout',
136
			];
137
138
			$message = 'Download failed';
139
			if(isset($statusCodes[$httpCode])) {
140
				$message .= ' - ' . $statusCodes[$httpCode] . ' (HTTP ' . $httpCode . ')';
141
			} else {
142
				$message .= ' - HTTP status code: ' . $httpCode;
143
			}
144
145
			$curlErrorMessage = curl_error($ch);
146
			if(!empty($curlErrorMessage)) {
147
				$message .= ' - curl error message: ' . $curlErrorMessage;
148
			}
149
			$message .= ' - URL: ' . htmlentities($url);
150
151
			throw new \Exception($message);
152
		}
153
154
		$info = curl_getinfo($ch);
155
		$this->logger->writeln("Download ".$info['size_download']." bytes");
156
157
		curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
		curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
158
		fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

158
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
159
	}
160
161
	/**
162
	 * @param string $in
163
	 * @param string $out
164
	 * @desc uncompressing the file with the bzip2-extension
165
	 *
166
	 * @throws \Exception
167
	 */
168
	private function bunzip2 ($in, $out) {
169
		$this->logger->writeln('Decompresing: '.$in. ' on '.$out);
170
		$this->logger->writeln('');
171
172
		if (!file_exists ($in) || !is_readable ($in))
173
			throw new \Exception('The file '.$in.' not exists or is not readable');
174
		if ((!file_exists ($out) && !is_writeable (dirname ($out)) || (file_exists($out) && !is_writable($out)) ))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! file_exists($out) && ... && ! is_writable($out), Probably Intended Meaning: ! file_exists($out) && (...&& ! is_writable($out))
Loading history...
175
			throw new \Exception('The file '.$out.' exists or is not writable');
176
177
		$in_file = bzopen ($in, "r");
178
		$out_file = fopen ($out, "w");
179
180
		while ($buffer = bzread ($in_file, 4096)) {
181
			if($buffer === FALSE)
182
				throw new \Exception('Read problem: ' . bzerrstr($in_file));
183
			if(bzerrno($in_file) !== 0)
184
				throw new \Exception('Compression problem: '. bzerrstr($in_file));
185
			fwrite ($out_file, $buffer, 4096);
0 ignored issues
show
Bug introduced by
It seems like $out_file can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
			fwrite (/** @scrutinizer ignore-type */ $out_file, $buffer, 4096);
Loading history...
186
		}
187
188
		bzclose ($in_file);
189
		fclose ($out_file);
0 ignored issues
show
Bug introduced by
It seems like $out_file can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

189
		fclose (/** @scrutinizer ignore-type */ $out_file);
Loading history...
190
	}
191
192
	private function getDownloadedFile (string $url): string {
193
		$file = $this->tempFolder . basename($url);
194
		return $file;
195
	}
196
197
}
198