ApiController::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 8
dl 0
loc 18
ccs 0
cts 8
cp 0
crap 2
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2021 Ming Tsang <[email protected]>
4
 * @copyright Copyright (c) 2022 Matias De lellis <[email protected]>
5
 *
6
 * @author Ming Tsang <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OCA\FaceRecognition\Controller;
26
27
use OCP\IRequest;
28
use OCP\Files\File;
29
30
use OCP\AppFramework\Http;
31
use OCP\AppFramework\Http\JSONResponse;
32
use OCP\AppFramework\ApiController as NCApiController;
33
34
use OCA\FaceRecognition\Db\Face;
35
use OCA\FaceRecognition\Db\FaceMapper;
36
37
use OCA\FaceRecognition\Db\Image;
38
use OCA\FaceRecognition\Db\ImageMapper;
39
40
use OCA\FaceRecognition\Db\Person;
41
use OCA\FaceRecognition\Db\PersonMapper;
42
43
use OCA\FaceRecognition\Service\SettingsService;
44
use OCA\FaceRecognition\Service\UrlService;
45
46
class ApiController extends NcApiController {
47
48
	/** @var FaceMapper */
49
	private $faceMapper;
50
51
	/** @var ImageMapper */
52
	private $imageMapper;
53
54
	/** @var PersonMapper */
55
	private $personMapper;
56
57
	/** @var SettingsService */
58
	private $settingsService;
59
60
	/** @var UrlService */
61
	private $urlService;
62
63
	/** @var string */
64
	private $userId;
65
66
	public function __construct(
67
		$AppName,
68
		IRequest        $request,
69
		FaceMapper      $faceMapper,
70
		ImageMapper     $imageMapper,
71
		PersonMapper    $personmapper,
72
		SettingsService $settingsService,
73
		UrlService      $urlService,
74
		$UserId)
75
	{
76
		parent::__construct($AppName, $request);
77
78
		$this->faceMapper      = $faceMapper;
79
		$this->imageMapper     = $imageMapper;
80
		$this->personMapper    = $personmapper;
81
		$this->settingsService = $settingsService;
82
		$this->urlService      = $urlService;
83
		$this->userId          = $UserId;
84
	}
85
86
	/**
87
	 * API V1
88
	 */
89
90
	/**
91
	 * Get all named persons
92
	 *
93
	 * - Endpoint: /persons
94
	 * - Method: GET
95
	 * - Response: Array of persons
96
	 * 		- Person:
97
	 * 			- name: Name of the person
98
	 * 			- thumbFaceId: Face representing this person
99
	 * 			- count: Number of images associated to this person
100
	 *
101
	 * @NoAdminRequired
102
	 *
103
	 * @return JSONResponse
104
	 */
105
	public function getPersons(): JSONResponse {
106
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
107
108
		$resp = array();
109
110
		if (!$userEnabled)
111
			return new JSONResponse($resp);
112
113
		$modelId = $this->settingsService->getCurrentFaceModel();
114
115
		$personsNames = $this->personMapper->findDistinctNames($this->userId, $modelId);
116
		foreach ($personsNames as $personNamed) {
117
			$facesCount = 0;
118
			$thumbFaceId = null;
119
			$persons = $this->personMapper->findByName($this->userId, $modelId, $personNamed->getName());
120
			foreach ($persons as $person) {
121
				$personFaces = $this->faceMapper->findFromCluster($this->userId, $person->getId(), $modelId);
122
				if (is_null($thumbFaceId)) {
123
					$thumbFaceId = $personFaces[0]->getId();
124
				}
125
				$facesCount += count($personFaces);
126
			}
127
128
			$respPerson = [];
129
			$respPerson['name'] = $personNamed->getName();
130
			$respPerson['thumbFaceId'] = $thumbFaceId;
131
			$respPerson['count'] = $facesCount;
132
133
			$resp[] = $respPerson;
134
		}
135
136
		return new JSONResponse($resp);
137
	}
138
139
	/**
140
	 * Get all faces associated to a person
141
	 *
142
	 * - Endpoint: /person/<name>/faces
143
	 * - Method: GET
144
	 * - URL Arguments: name - (string) name of the person
145
	 * - Response: Array of faces
146
	 * 		- Face:
147
	 * 			- id: Face ID
148
	 * 			- fileId: The file where this face was found
149
	 *
150
	 * @NoAdminRequired
151
	 *
152
	 * @return JSONResponse
153
	 */
154
	public function getFacesByPerson(string $name): JSONResponse {
155
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
156
157
		$resp = array();
158
159
		if (!$userEnabled)
160
			return new JSONResponse($resp);
161
162
		$modelId = $this->settingsService->getCurrentFaceModel();
163
164
		$clusters = $this->personMapper->findByName($this->userId, $modelId, $name);
165
		foreach ($clusters as $cluster) {
166
			$faces = $this->faceMapper->findFromCluster($this->userId, $cluster->getId(), $modelId);
167
			foreach ($faces as $face) {
168
				$image = $this->imageMapper->find($this->userId, $face->getImage());
169
170
				$respFace = [];
171
				$respFace['id'] = $face->getId();
172
				$respFace['fileId'] = $image->getFile();
173
174
				$resp[] = $respFace;
175
			}
176
		}
177
178
		return new JSONResponse($resp);
179
	}
180
181
	/**
182
	 * API V2
183
	 */
184
185
	/**
186
	 * @NoAdminRequired
187
	 * @CORS
188
	 * @NoCSRFRequired
189
	 *
190
	 * @return JSONResponse
191
	 */
192
	public function getPersonsV2($thumb_size = 128): JSONResponse {
193
		if (!$this->settingsService->getUserEnabled($this->userId))
194
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
195
196
		$list = [];
197
		$modelId = $this->settingsService->getCurrentFaceModel();
198
		$personsNames = $this->personMapper->findDistinctNames($this->userId, $modelId);
199
		foreach ($personsNames as $personNamed) {
200
			$name = $personNamed->getName();
201
			$personFace = current($this->faceMapper->findFromPerson($this->userId, $name, $modelId, 1));
202
203
			$person = [];
204
			$person['name'] = $name;
205
			$person['thumbUrl'] = $this->urlService->getThumbUrl($personFace->getId(), $thumb_size);
206
			$person['count'] = $this->imageMapper->countFromPerson($this->userId, $modelId, $name);
207
208
			$list[] = $person;
209
		}
210
211
		return new JSONResponse($list, Http::STATUS_OK);
212
	}
213
214
	/**
215
	 * @NoAdminRequired
216
	 * @CORS
217
	 * @NoCSRFRequired
218
	 *
219
	 * @return JSONResponse
220
	 */
221
	public function getPerson(string $personName, $thumb_size = 128): JSONResponse {
222
		if (!$this->settingsService->getUserEnabled($this->userId))
223
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
224
		if (empty($personName))
225
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
226
227
		$resp = [];
228
		$resp['name'] = $personName;
229
		$resp['thumbUrl'] = null;
230
		$resp['images'] = array();
231
232
		$modelId = $this->settingsService->getCurrentFaceModel();
233
234
		$personFace = current($this->faceMapper->findFromPerson($this->userId, $personName, $modelId, 1));
235
		$resp['thumbUrl'] = $this->urlService->getThumbUrl($personFace->getId(), $thumb_size);
236
237
		$images = $this->imageMapper->findFromPerson($this->userId, $modelId, $personName);
238
		foreach ($images as $image) {
239
			$node = $this->urlService->getFileNode($image->getFile());
240
			if ($node === null) continue;
241
242
			$photo = [];
243
			$photo['basename'] = $this->urlService->getBasename($node);
244
			$photo['filename'] = $this->urlService->getFilename($node);
245
			$photo['mimetype'] = $this->urlService->getMimetype($node);
246
			$photo['fileUrl']  = $this->urlService->getRedirectToFileUrl($node);
247
			$photo['thumbUrl'] = $this->urlService->getPreviewUrl($node, 256);
248
249
			$resp['images'][] = $photo;
250
		}
251
252
		return new JSONResponse($resp, Http::STATUS_OK);
253
	}
254
255
	/**
256
	 * @NoAdminRequired
257
	 * @CORS
258
	 * @NoCSRFRequired
259
	 *
260
	 * @return JSONResponse
261
	 */
262
	public function updatePerson(string $personName, $name = null, $visible = null): JSONResponse {
263
		if (!$this->settingsService->getUserEnabled($this->userId))
264
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
265
		if (empty($personName))
266
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
267
268
		$modelId = $this->settingsService->getCurrentFaceModel();
269
270
		$clusters = $this->personMapper->findByName($this->userId, $modelId, $personName);
271
		if (empty($clusters))
272
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
273
274
		if (!is_null($name)) {
275
			foreach ($clusters as $person) {
276
				$person->setName($name);
277
				$this->personMapper->update($person);
278
			}
279
		}
280
		// When change visibility it has a special treatment
281
		if (!is_null($visible)) {
282
			foreach ($clusters as $person) {
283
				$person->setIsVisible($visible);
284
				$person->setName($visible ? $name : null);
285
				$this->personMapper->update($person);
286
			}
287
		}
288
289
		// FIXME: What should response?
290
		if (is_null($name) || (!is_null($visible) && !$visible))
291
			return new JSONResponse([], Http::STATUS_OK);
292
		else
293
			return $this->getPerson($name);
294
	}
295
296
	/**
297
	 * @NoAdminRequired
298
	 * @CORS
299
	 * @NoCSRFRequired
300
	 *
301
	 * @return JSONResponse
302
	 */
303
	public function updateCluster(int $clusterId, $name = null, $visible = null): JSONResponse {
304
		if (!$this->settingsService->getUserEnabled($this->userId))
305
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
306
307
		$cluster = [];
308
		if (!is_null($name)) {
309
			$cluster = $this->personMapper->find($this->userId, $clusterId);
310
			$cluster->setName($name);
311
			$cluster = $this->personMapper->update($cluster);
312
		}
313
314
		if (!is_null($visible)) {
315
			$cluster = $this->personMapper->find($this->userId, $clusterId);
316
			$cluster->setIsVisible($visible);
317
			$cluster->setName($visible ? $name : null);
318
			$cluster = $this->personMapper->update($cluster);
319
		}
320
321
		// FIXME: What should response?
322
		return new JSONResponse($cluster, Http::STATUS_OK);
323
	}
324
325
	/**
326
	 * @NoAdminRequired
327
	 * @CORS
328
	 * @NoCSRFRequired
329
	 *
330
	 * @return JSONResponse
331
	 */
332
	public function discoverPerson($minimum_count = NULL, $max_previews = 40, $thumb_size = 128): JSONResponse {
333
		if (!$this->settingsService->getUserEnabled($this->userId))
334
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
335
336
		$discoveries = [];
337
338
		$modelId = $this->settingsService->getCurrentFaceModel();
339
		if (is_null($minimum_count))
340
			$minimum_count = $this->settingsService->getMinimumFacesInCluster();
341
342
		$clusters = $this->personMapper->findUnassigned($this->userId, $modelId);
343
		foreach ($clusters as $cluster) {
344
			$clusterSize = $this->personMapper->countClusterFaces($cluster->getId());
345
			if ($clusterSize < $minimum_count)
346
				continue;
347
348
			$personFaces = $this->faceMapper->findFromCluster($this->userId, $cluster->getId(), $modelId, $max_previews);
349
350
			$faces = [];
351
			foreach ($personFaces as $personFace) {
352
				$image = $this->imageMapper->find($this->userId, $personFace->getImage());
353
354
				$file = $this->urlService->getFileNode($image->getFile());
355
				if ($file === null) continue;
356
357
				$face = [];
358
				$face['thumbUrl'] = $this->urlService->getThumbUrl($personFace->getId(), $thumb_size);
359
				$face['fileUrl'] = $this->urlService->getRedirectToFileUrl($file);
360
361
				$faces[] = $face;
362
			}
363
364
			$discovery = [];
365
			$discovery['id'] = $cluster->getId();
366
			$discovery['count'] = $clusterSize;
367
			$discovery['faces'] = $faces;
368
369
			$discoveries[] = $discovery;
370
		}
371
372
		usort($discoveries, function ($a, $b) {
373
			return $b['count'] <=> $a['count'];
374
		});
375
376
		return new JSONResponse($discoveries, Http::STATUS_OK);
377
	}
378
379
	/**
380
	 * @NoAdminRequired
381
	 * @CORS
382
	 * @NoCSRFRequired
383
	 *
384
	 * @return JSONResponse
385
	 */
386
	public function autocomplete(string $query, $thumb_size = 128): JSONResponse {
387
		if (!$this->settingsService->getUserEnabled($this->userId))
388
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
389
390
		if (strlen($query) < 3)
391
			return new JSONResponse([], Http::STATUS_OK);
392
393
		$resp = [];
394
395
		$modelId = $this->settingsService->getCurrentFaceModel();
396
		$persons = $this->personMapper->findPersonsLike($this->userId, $modelId, $query);
397
		foreach ($persons as $person) {
398
			$name = [];
399
			$name['name'] = $person->getName();
400
			$name['value'] = $person->getName();
401
402
			$personFace = current($this->faceMapper->findFromPerson($this->userId, $person->getName(), $modelId, 1));
403
			$name['thumbUrl'] = $this->urlService->getThumbUrl($personFace->getId(), $thumb_size);
404
405
			$resp[] = $name;
406
		}
407
408
		return new JSONResponse($resp, Http::STATUS_OK);
409
	}
410
411
	/**
412
	 * @NoAdminRequired
413
	 * @CORS
414
	 * @NoCSRFRequired
415
	 *
416
	 * @return JSONResponse
417
	 */
418
	public function detachFace(int $faceId, $name = null): JSONResponse {
419
		if (!$this->settingsService->getUserEnabled($this->userId))
420
			return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
421
422
		$face = $this->faceMapper->find($faceId);
423
		$person = $this->personMapper->detachFace($face->getPerson(), $faceId, $name);
424
		return new JSONResponse($person, Http::STATUS_OK);
425
	}
426
427
}
428