Passed
Push — visibilities ( ff77ec...bb2343 )
by Matias
05:34
created

PersonMapper::countClusterFaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 1
dl 0
loc 12
ccs 0
cts 10
cp 0
crap 2
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2018-2021, Matias De lellis <[email protected]>
4
 * @copyright Copyright (c) 2018-2019, Branko Kokanovic <[email protected]>
5
 *
6
 * @author Branko Kokanovic <[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
namespace OCA\FaceRecognition\Db;
25
26
use OC\DB\QueryBuilder\Literal;
0 ignored issues
show
Bug introduced by
The type OC\DB\QueryBuilder\Literal 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...
27
28
use OCP\IDBConnection;
29
use OCP\IUser;
30
31
use OCP\AppFramework\Db\QBMapper;
32
use OCP\AppFramework\Db\DoesNotExistException;
33
use OCP\DB\QueryBuilder\IQueryBuilder;
34
35
class PersonMapper extends QBMapper {
36
37 1
	public function __construct(IDBConnection $db) {
38 1
		parent::__construct($db, 'facerecog_persons', '\OCA\FaceRecognition\Db\Person');
39 1
	}
40
41
	/**
42
	 * @param string $userId ID of the user
43
	 * @param int $personId ID of the person
44
	 *
45
	 * @return Person
46
	 */
47 8
	public function find(string $userId, int $personId): Person {
48 8
		$qb = $this->db->getQueryBuilder();
49 8
		$qb->select('id', 'name')
50 8
			->from($this->getTableName(), 'p')
51 8
			->where($qb->expr()->eq('id', $qb->createNamedParameter($personId)))
52 8
			->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($userId)));
53 8
		return $this->findEntity($qb);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity($qb) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\FaceRecognition\Db\Person.
Loading history...
54
	}
55
56
	/**
57
	 * @param string $userId ID of the user
58
	 * @param int $modelId ID of the model
59
	 * @param string $personName name of the person to find
60
	 * @return Person[]
61
	 */
62 2
	public function findByName(string $userId, int $modelId, string $personName): array {
63 2
		$sub = $this->db->getQueryBuilder();
64 2
		$sub->select(new Literal('1'))
65 2
			->from('facerecog_faces', 'f')
66 2
			->innerJoin('f', 'facerecog_images' ,'i', $sub->expr()->eq('f.image', 'i.id'))
67 2
			->where($sub->expr()->eq('p.id', 'f.person'))
68 2
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id')))
69 2
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model_id')))
70 2
			->andWhere($sub->expr()->eq('p.name', $sub->createParameter('person_name')));
71
72 2
		$qb = $this->db->getQueryBuilder();
73 2
		$qb->select('id', 'name', 'is_valid')
74 2
			->from($this->getTableName(), 'p')
75 2
			->where('EXISTS (' . $sub->getSQL() . ')')
76 2
			->setParameter('user_id', $userId)
77 2
			->setParameter('model_id', $modelId)
78 2
			->setParameter('person_name', $personName);
79
80 2
		return $this->findEntities($qb);
81
	}
82
83
	/**
84
	 * @param string $userId ID of the user
85
	 * @param int $modelId ID of the model
86
	 * @return Person[]
87
	 */
88
	public function findUnassigned(string $userId, int $modelId): array {
89
		$sub = $this->db->getQueryBuilder();
90
		$sub->select(new Literal('1'))
91
			->from('facerecog_faces', 'f')
92
			->innerJoin('f', 'facerecog_images' ,'i', $sub->expr()->eq('f.image', 'i.id'))
93
			->where($sub->expr()->eq('p.id', 'f.person'))
94
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id')))
95
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model_id')));
96
97
		$qb = $this->db->getQueryBuilder();
98
		$qb->select('id', 'is_valid')
99
			->from($this->getTableName(), 'p')
100
			->where('EXISTS (' . $sub->getSQL() . ')')
101
			->andWhere($qb->expr()->eq('is_valid', $qb->createParameter('is_valid')))
102
			->andWhere($qb->expr()->eq('is_visible', $qb->createParameter('is_visible')))
103
			->andWhere($qb->expr()->isNull('name'))
104
			->setParameter('user_id', $userId)
105
			->setParameter('model_id', $modelId)
106
			->setParameter('is_valid', true, IQueryBuilder::PARAM_BOOL)
107
			->setParameter('is_visible', true, IQueryBuilder::PARAM_BOOL);
108
109
		return $this->findEntities($qb);
110
	}
111
112
	/**
113
	 * @param string $userId ID of the user
114
	 * @param int $modelId ID of the model
115
	 * @return Person[]
116
	 */
117 13
	public function findAll(string $userId, int $modelId): array {
118 13
		$sub = $this->db->getQueryBuilder();
119 13
		$sub->select(new Literal('1'))
120 13
			->from('facerecog_faces', 'f')
121 13
			->innerJoin('f', 'facerecog_images' ,'i', $sub->expr()->eq('f.image', 'i.id'))
122 13
			->where($sub->expr()->eq('p.id', 'f.person'))
123 13
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id')))
124 13
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model_id')));
125
126 13
		$qb = $this->db->getQueryBuilder();
127 13
		$qb->select('id', 'name', 'is_valid')
128 13
			->from($this->getTableName(), 'p')
129 13
			->where('EXISTS (' . $sub->getSQL() . ')')
130 13
			->setParameter('user_id', $userId)
131 13
			->setParameter('model_id', $modelId);
132
133 13
		return $this->findEntities($qb);
134
	}
135
136
	/**
137
	 * @param string $userId ID of the user
138
	 *
139
	 * @return Person[]
140
	 */
141
	public function findDistinctNames(string $userId, int $modelId): array {
142
		$qb = $this->db->getQueryBuilder();
143
		$qb->selectDistinct('name')
144
			->from($this->getTableName(), 'p')
145
			->innerJoin('p', 'facerecog_faces' , 'f', $qb->expr()->eq('f.person', 'p.id'))
146
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
147
			->where($qb->expr()->eq('i.user', $qb->createParameter('user_id')))
148
			->andWhere($qb->expr()->eq('i.model', $qb->createParameter('model_id')))
149
			->andwhere($qb->expr()->isNotNull('p.name'))
150
			->setParameter('user_id', $userId)
151
			->setParameter('model_id', $modelId);
152
		return $this->findEntities($qb);
153
	}
154
155
	/**
156
	 * Search Person by name
157
	 *
158
	 * @param int|null $offset
159
	 * @param int|null $limit
160
	 */
161
	public function findPersonsLike(string $userId, int $modelId, string $name, ?int $offset = null, ?int $limit = null): array {
162
		$qb = $this->db->getQueryBuilder();
163
		$qb->selectDistinct('p.name')
164
			->from($this->getTableName(), 'p')
165
			->innerJoin('p', 'facerecog_faces', 'f', $qb->expr()->eq('f.person', 'p.id'))
166
			->innerJoin('p', 'facerecog_images', 'i', $qb->expr()->eq('f.image', 'i.id'))
167
			->where($qb->expr()->eq('p.user', $qb->createNamedParameter($userId)))
168
			->andWhere($qb->expr()->eq('model', $qb->createNamedParameter($modelId)))
169
			->andWhere($qb->expr()->eq('is_processed', $qb->createNamedParameter(True)))
170
			->andWhere($qb->expr()->like($qb->func()->lower('p.name'), $qb->createParameter('query')));
171
172
		$query = '%' . $this->db->escapeLikeParameter(strtolower($name)) . '%';
173
		$qb->setParameter('query', $query);
174
175
		$qb->setFirstResult($offset);
176
		$qb->setMaxResults($limit);
177
178
		return $this->findEntities($qb);
179
	}
180
181
	/**
182
	 * Returns count of persons (clusters) found for a given user.
183
	 *
184
	 * @param string $userId ID of the user
185
	 * @param int $modelId ID of the model
186
	 * @param bool $onlyInvalid True if client wants count of invalid persons only,
187
	 *  false if client want count of all persons
188
	 * @return int Count of persons
189
	 */
190 15
	public function countPersons(string $userId, int $modelId, bool $onlyInvalid=false): int {
191 15
		$sub = $this->db->getQueryBuilder();
192 15
		$sub->select(new Literal('1'))
193 15
			->from('facerecog_faces', 'f')
194 15
			->innerJoin('f', 'facerecog_images' ,'i', $sub->expr()->eq('f.image', 'i.id'))
195 15
			->where($sub->expr()->eq('p.id', 'f.person'))
196 15
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id')))
197 15
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model_id')));
198
199 15
		$qb = $this->db->getQueryBuilder();
200 15
		$qb->select($qb->createFunction('COUNT(' . $qb->getColumnName('id') . ')'))
201 15
			->from($this->getTableName(), 'p')
202 15
			->where('EXISTS (' . $sub->getSQL() . ')');
203
204 15
		if ($onlyInvalid) {
205
			$qb = $qb
206
				->andWhere($qb->expr()->eq('is_valid', $qb->createParameter('is_valid')))
207
				->setParameter('is_valid', false, IQueryBuilder::PARAM_BOOL);
208
		}
209
210
		$qb = $qb
211 15
			->setParameter('user_id', $userId)
212 15
			->setParameter('model_id', $modelId);
213
214 15
		$resultStatement = $qb->execute();
215 15
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
216 15
		$resultStatement->closeCursor();
217
218 15
		return (int)$data[0];
219
	}
220
221
	/**
222
	 * Based on a given image, takes all faces that belong to that image
223
	 * and invalidates all person that those faces belongs to.
224
	 *
225
	 * @param int $imageId ID of image for which to invalidate persons for
226
	 *
227
	 * @return void
228
	 */
229 12
	public function invalidatePersons(int $imageId): void {
230 12
		$sub = $this->db->getQueryBuilder();
231 12
		$tableNameWithPrefixWithoutQuotes = trim($sub->getTableName($this->getTableName()), '`');
232 12
		$sub->select(new Literal('1'));
233 12
		$sub->from('facerecog_images', 'i')
234 12
			->innerJoin('i', 'facerecog_faces' ,'f', $sub->expr()->eq('i.id', 'f.image'))
235 12
			->where($sub->expr()->eq($tableNameWithPrefixWithoutQuotes . '.id', 'f.person'))
236 12
			->andWhere($sub->expr()->eq('i.id', $sub->createParameter('image_id')));
237
238 12
		$qb = $this->db->getQueryBuilder();
239 12
		$qb->update($this->getTableName())
240 12
			->set("is_valid", $qb->createParameter('is_valid'))
241 12
			->where('EXISTS (' . $sub->getSQL() . ')')
242 12
			->setParameter('image_id', $imageId)
243 12
			->setParameter('is_valid', false, IQueryBuilder::PARAM_BOOL)
244 12
			->execute();
245 12
	}
246
247
	/**
248
	 * Based on current clusters and new clusters, do database reconciliation.
249
	 * It tries to do that in minimal number of SQL queries. Operation is atomic.
250
	 *
251
	 * Clusters are array, where keys are ID of persons, and values are indexed arrays
252
	 * with values that are ID of the faces for those persons.
253
	 *
254
	 * @param string $userId ID of the user that clusters belong to
255
	 * @param array $currentClusters Current clusters
256
	 * @param array $newClusters New clusters
257
	 *
258
	 * @return void
259
	 */
260 14
	public function mergeClusterToDatabase(string $userId, $currentClusters, $newClusters): void {
261 14
		$this->db->beginTransaction();
262 14
		$currentDateTime = new \DateTime();
263
264
		try {
265
			// Delete clusters that do not exist anymore
266 14
			foreach($currentClusters as $oldPerson => $oldFaces) {
267 11
				if (array_key_exists($oldPerson, $newClusters)) {
268 6
					continue;
269
				}
270
271
				// OK, we bumped into cluster that existed and now it does not exist.
272
				// We need to remove all references to it and to delete it.
273 7
				foreach ($oldFaces as $oldFace) {
274 7
					$this->updateFace($oldFace, null);
275
				}
276
277
				// todo: this is not very cool. What if user had associated linked user to this. And all lost?
278 7
				$qb = $this->db->getQueryBuilder();
279
				// todo: for extra safety, we should probably add here additional condition, where (user=$userId)
280
				$qb
281 7
					->delete($this->getTableName())
282 7
					->where($qb->expr()->eq('id', $qb->createNamedParameter($oldPerson)))
283 7
					->execute();
284
			}
285
286
			// Modify existing clusters
287 14
			foreach($newClusters as $newPerson=>$newFaces) {
288 12
				if (!array_key_exists($newPerson, $currentClusters)) {
289
					// This cluster didn't exist, there is nothing to modify
290
					// It will be processed during cluster adding operation
291 9
					continue;
292
				}
293
294 6
				$oldFaces = $currentClusters[$newPerson];
295 6
				if ($newFaces === $oldFaces) {
296
					// Set cluster as valid now
297 2
					$qb = $this->db->getQueryBuilder();
298
					$qb
299 2
						->update($this->getTableName())
300 2
						->set("is_valid", $qb->createParameter('is_valid'))
301 2
						->where($qb->expr()->eq('id', $qb->createNamedParameter($newPerson)))
302 2
						->setParameter('is_valid', true, IQueryBuilder::PARAM_BOOL)
303 2
						->execute();
304 2
					continue;
305
				}
306
307
				// OK, set of faces do differ. Now, we could potentially go into finer grain details
308
				// and add/remove each individual face, but this seems too detailed. Enough is to
309
				// reset all existing faces to null and to add new faces to new person. That should
310
				// take care of both faces that are removed from cluster, as well as for newly added
311
				// faces to this cluster.
312
313
				// First remove all old faces from any cluster (reset them to null)
314 5
				foreach ($oldFaces as $oldFace) {
315
					// Reset face to null only if it wasn't moved to other cluster!
316
					// (if face is just moved to other cluster, do not reset to null, as some other
317
					// pass for some other cluster will eventually update it to proper cluster)
318 5
					if ($this->isFaceInClusters($oldFace, $newClusters) === false) {
319 1
						$this->updateFace($oldFace, null);
320
					}
321
				}
322
323
				// Then set all new faces to belong to this cluster
324 5
				foreach ($newFaces as $newFace) {
325 5
					$this->updateFace($newFace, $newPerson);
326
				}
327
328
				// Set cluster as valid now
329 5
				$qb = $this->db->getQueryBuilder();
330
				$qb
331 5
					->update($this->getTableName())
332 5
					->set("is_valid", $qb->createParameter('is_valid'))
333 5
					->where($qb->expr()->eq('id', $qb->createNamedParameter($newPerson)))
334 5
					->setParameter('is_valid', true, IQueryBuilder::PARAM_BOOL)
335 5
					->execute();
336
			}
337
338
			// Add new clusters
339 14
			foreach($newClusters as $newPerson=>$newFaces) {
340 12
				if (array_key_exists($newPerson, $currentClusters)) {
341
					// This cluster already existed, nothing to add
342
					// It was already processed during modify cluster operation
343 6
					continue;
344
				}
345
346
				// Create new cluster and add all faces to it
347 9
				$qb = $this->db->getQueryBuilder();
348
				$qb
349 9
					->insert($this->getTableName())
350 9
					->values([
351 9
						'user' => $qb->createNamedParameter($userId),
352 9
						'is_valid' => $qb->createNamedParameter(true),
353 9
						'last_generation_time' => $qb->createNamedParameter($currentDateTime, IQueryBuilder::PARAM_DATE),
354 9
						'linked_user' => $qb->createNamedParameter(null)])
355 9
					->execute();
356 9
				$insertedPersonId = $this->db->lastInsertId($this->getTableName());
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IDBConnection::lastInsertId() has been deprecated: 21.0.0 use \OCP\DB\QueryBuilder\IQueryBuilder::getLastInsertId ( Ignorable by Annotation )

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

356
				$insertedPersonId = /** @scrutinizer ignore-deprecated */ $this->db->lastInsertId($this->getTableName());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
357 9
				foreach ($newFaces as $newFace) {
358 9
					$this->updateFace($newFace, $insertedPersonId);
359
				}
360
			}
361
362 14
			$this->db->commit();
363
		} catch (\Exception $e) {
364
			$this->db->rollBack();
365
			throw $e;
366
		}
367 14
	}
368
369
	/**
370
	 * Deletes all persons from that user.
371
	 *
372
	 * @param string $userId User to drop persons from a table.
373
	 *
374
	 * @return void
375
	 */
376 28
	public function deleteUserPersons(string $userId): void {
377 28
		$qb = $this->db->getQueryBuilder();
378 28
		$qb->delete($this->getTableName())
379 28
			->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
380 28
			->execute();
381 28
	}
382
383
	/**
384
	 * Deletes all persons from that user and model
385
	 *
386
	 * @param string $userId ID of user for drop from table
387
	 * @param int $modelId
388
	 *
389
	 * @return void
390
	 */
391
	public function deleteUserModel(string $userId, int $modelId): void {
392
		//TODO: Make it atomic
393
		$qb = $this->db->getQueryBuilder();
394
		$qb->delete($this->getTableName())
395
			->where($qb->expr()->eq('id', $qb->createParameter('person')));
396
397
		$persons = $this->findAll($userId, $modelId);
398
		foreach ($persons as $person) {
399
			$qb->setParameter('person', $person->getId())->execute();
400
		}
401
	}
402
403
	/**
404
	 * Deletes person if it is empty (have no faces associated to it)
405
	 *
406
	 * @param int $personId Person to check if it should be deleted
407
	 *
408
	 * @return void
409
	 */
410
	public function removeIfEmpty(int $personId): void {
411
		$sub = $this->db->getQueryBuilder();
412
		$sub->select(new Literal('1'));
413
		$sub->from('facerecog_faces', 'f')
414
			->where($sub->expr()->eq('f.person', $sub->createParameter('person')));
415
416
		$qb = $this->db->getQueryBuilder();
417
		$qb->delete($this->getTableName())
418
			->where($qb->expr()->eq('id', $qb->createParameter('person')))
419
			->andWhere('NOT EXISTS (' . $sub->getSQL() . ')')
420
			->setParameter('person', $personId)
421
			->execute();
422
	}
423
424
	/**
425
	 * Deletes all persons that have no faces associated to them
426
	 *
427
	 * @param string $userId ID of user for which we are deleting orphaned persons
428
	 */
429 1
	public function deleteOrphaned(string $userId): int {
430 1
		$sub = $this->db->getQueryBuilder();
431 1
		$sub->select(new Literal('1'));
432 1
		$sub->from('facerecog_faces', 'f')
433 1
			->where($sub->expr()->eq('f.person', 'p.id'));
434
435 1
		$qb = $this->db->getQueryBuilder();
436 1
		$qb->select('p.id')
437 1
			->from($this->getTableName(), 'p')
438 1
			->where($qb->expr()->eq('p.user', $qb->createParameter('user')))
439 1
			->andWhere('NOT EXISTS (' . $sub->getSQL() . ')')
440 1
			->setParameter('user', $userId);
441 1
		$orphanedPersons = $this->findEntities($qb);
442
443 1
		$orphaned = 0;
444 1
		foreach ($orphanedPersons as $person) {
445
			$qb = $this->db->getQueryBuilder();
446
			$orphaned += $qb->delete($this->getTableName())
447
				->where($qb->expr()->eq('id', $qb->createNamedParameter($person->id)))
448
				->execute();
449
		}
450 1
		return $orphaned;
451
	}
452
453
	/*
454
	 * Mark the cluster as hidden or visible to user.
455
	 *
456
	 * @param int $personId ID of the person
457
	 * @param bool $visible visibility of the person
458
	 *
459
	 * @return void
460
	 */
461
	public function setVisibility (int $personId, bool $visible): void {
462
		$qb = $this->db->getQueryBuilder();
463
		if ($visible) {
464
			$qb->update($this->getTableName())
465
				->set('is_visible', $qb->createNamedParameter(1))
466
				->where($qb->expr()->eq('id', $qb->createNamedParameter($personId)))
467
				->execute();
468
		} else {
469
			$qb->update($this->getTableName())
470
				->set('is_visible', $qb->createNamedParameter(0))
471
				->set('name', $qb->createNamedParameter(null))
472
				->where($qb->expr()->eq('id', $qb->createNamedParameter($personId)))
473
				->execute();
474
		}
475
	}
476
477
	/*
478
	 * Mark the cluster as hidden or visible to user.
479
	 *
480
	 * @param int $personId ID of the person
481
	 * @param int $faceId visibility of the person
482
	 *
483
	 * @return void
484
	 */
485
	public function detachFace(int $personId, int $faceId, $name = null): void {
486
		$single = $this->countClusterFaces($personId) === 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $single is dead and can be removed.
Loading history...
487
488
		// Mark the face as non groupable.
489
		$qb = $this->db->getQueryBuilder();
490
		$qb->update('facerecog_faces')
491
			->set('is_groupable', $qb->createParameter('is_groupable'))
492
			->where($qb->expr()->eq('id', $qb->createNamedParameter($faceId)))
493
			->setParameter('is_groupable', false, IQueryBuilder::PARAM_BOOL)
494
			->execute();
495
496
		if ($this->countClusterFaces($personId) === 1) {
497
			// If cluster is an single face just rename it.
498
			$qb = $this->db->getQueryBuilder();
499
			$qb->update($this->getTableName())
500
				->set('name', $qb->createNamedParameter($name))
501
				->where($qb->expr()->eq('id', $qb->createNamedParameter($personId)))
502
				->execute();
503
		} else {
504
			// If there are other faces, must create a new perso for that face.
505
			$qb = $this->db->getQueryBuilder();
506
			$qb->select('user')
507
				->from($this->getTableName())
508
				->where($qb->expr()->eq('id', $qb->createNamedParameter($personId)));
509
			$oldPerson = $this->findEntity($qb);
510
511
			$qb = $this->db->getQueryBuilder();
512
			$qb->insert($this->getTableName())->values([
513
				'user' => $qb->createNamedParameter($oldPerson->getUser()),
514
				'name' => $qb->createNamedParameter($name),
515
				'is_valid' => $qb->createNamedParameter(true),
516
				'last_generation_time' => $qb->createNamedParameter(new \DateTime(), IQueryBuilder::PARAM_DATE),
517
				'linked_user' => $qb->createNamedParameter(null),
518
				'is_visible' => $qb->createNamedParameter(true)
519
			])->execute();;
520
			$newPersonId = $this->db->lastInsertId($this->getTableName());
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IDBConnection::lastInsertId() has been deprecated: 21.0.0 use \OCP\DB\QueryBuilder\IQueryBuilder::getLastInsertId ( Ignorable by Annotation )

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

520
			$newPersonId = /** @scrutinizer ignore-deprecated */ $this->db->lastInsertId($this->getTableName());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
521
522
			$qb = $this->db->getQueryBuilder();
523
			$qb->update('facerecog_faces')
524
				->set('person', $qb->createParameter('person'))
525
				->where($qb->expr()->eq('id', $qb->createNamedParameter($faceId)))
526
				->setParameter('person', $newPersonId)
527
				->execute();
528
		}
529
	}
530
531
	public function countClusterFaces(int $personId): int {
532
		$qb = $this->db->getQueryBuilder();
533
		$query = $qb
534
			->select($qb->createFunction('COUNT(' . $qb->getColumnName('id') . ')'))
535
			->from('facerecog_faces')
536
			->where($qb->expr()->eq('person', $qb->createParameter('person')))
537
			->setParameter('person', $personId);
538
		$resultStatement = $query->execute();
539
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
540
		$resultStatement->closeCursor();
541
542
		return (int)$data[0];
543
	}
544
545
	/**
546
	 * Updates one face with $faceId to database to person ID $personId.
547
	 *
548
	 * @param int $faceId ID of the face
549
	 * @param int|null $personId ID of the person
550
	 *
551
	 * @return void
552
	 */
553 12
	private function updateFace(int $faceId, $personId): void {
554 12
		$qb = $this->db->getQueryBuilder();
555 12
		$qb->update('facerecog_faces')
556 12
			->set("person", $qb->createNamedParameter($personId))
557 12
			->where($qb->expr()->eq('id', $qb->createNamedParameter($faceId)))
558 12
			->execute();
559 12
	}
560
561
	/**
562
	 * Checks if face with a given ID is in any cluster.
563
	 *
564
	 * @param int $faceId ID of the face to check
565
	 * @param array $cluster All clusters to check into
566
	 *
567
	 * @return bool True if face is found in any cluster, false otherwise.
568
	 */
569 5
	private function isFaceInClusters(int $faceId, array $clusters): bool {
570 5
		foreach ($clusters as $_=>$faces) {
571 5
			if (in_array($faceId, $faces)) {
572 5
				return true;
573
			}
574
		}
575 1
		return false;
576
	}
577
}
578