Passed
Push — split-cluster-person ( 7ada7e...098531 )
by Matias
05:26
created

PersonController::findUnassignedClusters()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 23
nc 5
nop 0
dl 0
loc 33
ccs 0
cts 26
cp 0
crap 30
rs 9.2408
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2018-2020 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
24
namespace OCA\FaceRecognition\Controller;
25
26
use OCP\IRequest;
27
use OCP\Files\IRootFolder;
28
use OCP\Files\File;
29
use OCP\IUserSession;
30
use OCP\IURLGenerator;
31
32
use OCP\AppFramework\Http;
33
use OCP\AppFramework\Http\DataResponse;
34
use OCP\AppFramework\Http\JSONResponse;
35
use OCP\AppFramework\Http\DataDisplayResponse;
36
use OCP\AppFramework\Controller;
37
38
use OCA\FaceRecognition\Db\Face;
39
use OCA\FaceRecognition\Db\FaceMapper;
40
41
use OCA\FaceRecognition\Db\Image;
42
use OCA\FaceRecognition\Db\ImageMapper;
43
44
use OCA\FaceRecognition\Db\Person;
45
use OCA\FaceRecognition\Db\PersonMapper;
46
47
use OCA\FaceRecognition\Service\SettingsService;
48
49
50
class PersonController extends Controller {
51
52
	/** @var IRootFolder */
53
	private $rootFolder;
54
55
	/** @var IUserSession */
56
	private $userSession;
57
58
	/** @var IURLGenerator */
59
	private $urlGenerator;
60
61
	/** @var FaceMapper */
62
	private $faceMapper;
63
64
	/** @var ImageMapper */
65
	private $imageMapper;
66
67
	/** @var PersonMapper */
68
	private $personMapper;
69
70
	/** @var SettingsService */
71
	private $settingsService;
72
73
	/** @var string */
74
	private $userId;
75
76
	public function __construct($AppName,
77
	                            IRequest        $request,
78
	                            IRootFolder     $rootFolder,
79
	                            IUserSession    $userSession,
80
	                            IURLGenerator   $urlGenerator,
81
	                            FaceMapper      $faceMapper,
82
	                            ImageMapper     $imageMapper,
83
	                            PersonMapper    $personmapper,
84
	                            SettingsService $settingsService,
85
	                            $UserId)
86
	{
87
		parent::__construct($AppName, $request);
88
89
		$this->rootFolder      = $rootFolder;
90
		$this->userSession     = $userSession;
91
		$this->urlGenerator    = $urlGenerator;
92
		$this->faceMapper      = $faceMapper;
93
		$this->imageMapper     = $imageMapper;
94
		$this->personMapper    = $personmapper;
95
		$this->settingsService = $settingsService;
96
		$this->userId          = $UserId;
97
	}
98
99
	/**
100
	 * @NoAdminRequired
101
	 */
102
	public function index() {
103
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
104
105
		$resp = array();
106
		$resp['enabled'] = $userEnabled;
107
		$resp['persons'] = array();
108
109
		if (!$userEnabled)
110
			return new DataResponse($resp);
111
112
		$modelId = $this->settingsService->getCurrentFaceModel();
113
114
		$personsNames = $this->personMapper->findDistinctNames($this->userId, $modelId);
115
		foreach ($personsNames as $personNamed) {
116
			$facesCount = 0;
117
			$faceUrl = null;
118
			$faces = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $faces is dead and can be removed.
Loading history...
119
			$persons = $this->personMapper->findByName($this->userId, $modelId, $personNamed->getName());
120
			foreach ($persons as $person) {
121
				$personFaces = $this->faceMapper->findFacesFromPerson($this->userId, $person->getId(), $modelId);
122
				if (is_null($faceUrl)) {
123
					$faceUrl = $this->getThumbUrl($personFaces[0]->getId(), 128);
124
				}
125
				$facesCount += count($personFaces);
126
			}
127
128
			$person = [];
129
			$person['name'] = $personNamed->getName();
130
			$person['thumbUrl'] = $faceUrl;
131
			$person['count'] = $facesCount;
132
133
			$resp['persons'][] = $person;
134
		}
135
		return new DataResponse($resp);
136
	}
137
138
	/**
139
	 * @NoAdminRequired
140
	 */
141
	public function find(int $id) {
142
		$person = $this->personMapper->find($this->userId, $id);
143
144
		$resp = [];
145
		$faces = [];
146
		$personFaces = $this->faceMapper->findFacesFromPerson($this->userId, $person->getId(), $this->settingsService->getCurrentFaceModel());
147
		foreach ($personFaces as $personFace) {
148
			$image = $this->imageMapper->find($this->userId, $personFace->getImage());
149
			$fileId = $image->getFile();
150
			if ($fileId === null) continue;
151
152
			$fileUrl = $this->getRedirectToFileUrl($fileId);
153
			if ($fileUrl === null) continue;
154
155
			$face = [];
156
			$face['thumbUrl'] = $this->getThumbUrl($personFace->getId());
0 ignored issues
show
Bug introduced by
The call to OCA\FaceRecognition\Cont...ntroller::getThumbUrl() has too few arguments starting with size. ( Ignorable by Annotation )

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

156
			/** @scrutinizer ignore-call */ 
157
   $face['thumbUrl'] = $this->getThumbUrl($personFace->getId());

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
157
			$face['fileUrl'] = $fileUrl;
158
			$faces[] = $face;
159
		}
160
		$resp['name'] = $person->getName();
161
		$resp['id'] = $person->getId();
162
		$resp['faces'] = $faces;
163
164
		return new DataResponse($resp);
165
	}
166
167
	/**
168
	 * @NoAdminRequired
169
	 */
170
	public function findByName(string $personName) {
171
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
172
173
		$resp = array();
174
		$resp['enabled'] = $userEnabled;
175
		$resp['name'] = $personName;
176
		$resp['clusters'] = 0;
177
		$resp['images'] = array();
178
179
		if (!$userEnabled)
180
			return new DataResponse($resp);
181
182
		$modelId = $this->settingsService->getCurrentFaceModel();
183
184
		$clusters = $this->personMapper->findByName($this->userId, $modelId, $personName);
185
		foreach ($clusters as $cluster) {
186
			$resp['clusters']++;
187
188
			$faces = $this->faceMapper->findFacesFromPerson($this->userId, $cluster->getId(), $modelId);
189
			foreach ($faces as $face) {
190
				$image = $this->imageMapper->find($this->userId, $face->getImage());
191
192
				$fileId = $image->getFile();
193
				if ($fileId === null) continue;
194
195
				$fileUrl = $this->getRedirectToFileUrl($fileId);
196
				if ($fileUrl === null) continue;
197
198
				$thumbUrl = $this->getPreviewUrl($fileId, 256);
199
				if ($thumbUrl === null) continue;
200
201
				$image = [];
202
				$image['thumbUrl'] = $thumbUrl;
203
				$image['fileUrl'] = $fileUrl;
204
205
				$resp['images'][] = $image;
206
			}
207
		}
208
209
		return new DataResponse($resp);
210
	}
211
212
	/**
213
	 * @NoAdminRequired
214
	 */
215
	public function findClustersByName(string $personName) {
216
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
217
218
		$resp = array();
219
		$resp['enabled'] = $userEnabled;
220
		$resp['clusters'] = array();
221
222
		if (!$userEnabled)
223
			return new DataResponse($resp);
224
225
		$modelId = $this->settingsService->getCurrentFaceModel();
226
227
		$persons = $this->personMapper->findByName($this->userId, $modelId, $personName);
228
		foreach ($persons as $person) {
229
			$personFaces = $this->faceMapper->findFacesFromPerson($this->userId, $person->getId(), $modelId);
230
231
			$faces = [];
232
			foreach ($personFaces as $personFace) {
233
				$image = $this->imageMapper->find($this->userId, $personFace->getImage());
234
				$fileId = $image->getFile();
235
				if ($fileId === null) continue;
236
237
				$fileUrl = $this->getRedirectToFileUrl($fileId);
238
				if ($fileUrl === null) continue;
239
240
				$face = [];
241
				$face['thumbUrl'] = $this->getThumbUrl($personFace->getId(), 50);
242
				$face['fileUrl'] = $fileUrl;
243
				$faces[] = $face;
244
			}
245
246
			$cluster = [];
247
			$cluster['name'] = $person->getName();
248
			$cluster['count'] = count($personFaces);
249
			$cluster['id'] = $person->getId();
250
			$cluster['faces'] = $faces;
251
			$resp['clusters'][] = $cluster;
252
		}
253
254
		return new DataResponse($resp);
255
	}
256
257
	/**
258
	 * @NoAdminRequired
259
	 *
260
	 */
261
	public function findUnassignedClusters() {
262
		$userEnabled = $this->settingsService->getUserEnabled($this->userId);
263
264
		$resp = array();
265
		$resp['enabled'] = $userEnabled;
266
		$resp['clusters'] = array();
267
268
		if (!$userEnabled)
269
			return new DataResponse($resp);
270
271
		$modelId = $this->settingsService->getCurrentFaceModel();
272
273
		$persons = $this->personMapper->findUnassigned($this->userId, $modelId);
274
		foreach ($persons as $person) {
275
			$personFaces = $this->faceMapper->findFacesFromPerson($this->userId, $person->getId(), $modelId);
276
			if (count($personFaces) === 1)
277
				continue;
278
279
			$faces = [];
280
			foreach ($personFaces as $personFace) {
281
				$face = [];
282
				$face['thumbUrl'] = $this->getThumbUrl($personFace->getId(), 50);
283
				$faces[] = $face;
284
			}
285
286
			$cluster = [];
287
			$cluster['count'] = count($personFaces);
288
			$cluster['id'] = $person->getId();
289
			$cluster['faces'] = $faces;
290
			$resp['clusters'][] = $cluster;
291
		}
292
293
		return new DataResponse($resp);
294
	}
295
296
	/**
297
	 * @NoAdminRequired
298
	 *
299
	 * @param string $personName
300
	 * @param string $name
301
	 */
302
	public function updatePerson($personName, $name) {
303
		$modelId = $this->settingsService->getCurrentFaceModel();
304
		$clusters = $this->personMapper->findByName($this->userId, $modelId, $personName);
305
		foreach ($clusters as $person) {
306
			$person->setName($name);
307
			$this->personMapper->update($person);
308
		}
309
		return $this->findByName($name);
310
	}
311
312
	/**
313
	 * @NoAdminRequired
314
	 *
315
	 * @param int $id
316
	 * @param string $name
317
	 */
318
	public function updateName($id, $name) {
319
		$person = $this->personMapper->find ($this->userId, $id);
320
		$person->setName($name);
321
		$this->personMapper->update($person);
322
323
		$newPerson = $this->personMapper->find($this->userId, $id);
324
		return new DataResponse($newPerson);
325
	}
326
327
	/**
328
	 * Url to thumb face
329
	 *
330
	 * @param int $faceId face id to show
331
	 * @param int $size Size of face thumbnails
332
	 */
333
	private function getThumbUrl(int $faceId, int $size) {
334
		$params = [];
335
		$params['id'] = $faceId;
336
		$params['size'] = $size;
337
		return $this->urlGenerator->linkToRoute('facerecognition.face.getThumb', $params);
338
	}
339
340
	/**
341
	 * Get thumbnail of the give file id
342
	 *
343
	 * @param int $fileId file id to show
344
	 * @param int $sideSize side lenght to show
345
	 */
346
	public function getPreviewUrl(int $fileId, int $sideSize): ?string {
347
		$userFolder = $this->rootFolder->getUserFolder($this->userId);
348
		$file = current($userFolder->getById($fileId));
349
350
		if (!($file instanceof File)) {
351
			// If we cannot find a file probably it was deleted out of our control and we must clean our tables.
352
			$this->settingsService->setNeedRemoveStaleImages(true, $this->userId);
353
			return null;
354
		}
355
356
		return '/core/preview?fileId=' . $fileId .'&x=' . $sideSize . '&y=' . $sideSize . '&a=false&v=' . $file->getETag();
357
	}
358
359
	/**
360
	 * Redirects to the file list and highlight the given file id
361
	 *
362
	 * @param int $fileId file id to show
363
	 */
364
	private function getRedirectToFileUrl(int $fileId) {
365
		$uid        = $this->userSession->getUser()->getUID();
366
		$baseFolder = $this->rootFolder->getUserFolder($uid);
367
		$files      = $baseFolder->getById($fileId);
368
		$file       = current($files);
369
370
		if(!($file instanceof File)) {
371
			// If we cannot find a file probably it was deleted out of our control and we must clean our tables.
372
			$this->settingsService->setNeedRemoveStaleImages(true, $this->userId);
373
			return null;
374
		}
375
376
		$params = [];
377
		$params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
378
		$params['scrollto'] = $file->getName();
379
380
		return $this->urlGenerator->linkToRoute('files.view.index', $params);
381
	}
382
383
}
384