Passed
Pull Request — master (#442)
by Matias
07:21 queued 05:22
created

FaceMapper::deleteUserModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 12
nc 1
nop 2
dl 0
loc 14
ccs 0
cts 13
cp 0
crap 2
rs 9.8666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017-2020, Matias De lellis <[email protected]>
4
 * @copyright Copyright (c) 2018-2019, Branko Kokanovic <[email protected]>
5
 *
6
 * @author Matias De lellis <[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\Db;
26
27
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...
28
29
use OCP\IDBConnection;
30
use OCP\AppFramework\Db\QBMapper;
31
use OCP\AppFramework\Db\DoesNotExistException;
32
use OCP\DB\QueryBuilder\IQueryBuilder;
33
34
class FaceMapper extends QBMapper {
35
36
	public function __construct(IDBConnection $db) {
37
		parent::__construct($db, 'facerecog_faces', '\OCA\FaceRecognition\Db\Face');
38
	}
39
40 1
	public function find (int $faceId) {
41 1
		$qb = $this->db->getQueryBuilder();
42 1
		$qb->select('id', 'image', 'person', 'left', 'right', 'top', 'bottom', 'landmarks', 'descriptor', 'confidence')
43 1
			->from($this->getTableName(), 'f')
44 1
			->andWhere($qb->expr()->eq('id', $qb->createNamedParameter($faceId)));
45
		try {
46 1
			return $this->findEntity($qb);
47
		} catch (DoesNotExistException $e) {
48
			return null;
49
		}
50
	}
51
52
	/**
53
	 * Based on a given fileId, takes all faces that belong to that file
54
	 * and return an array with that.
55
	 *
56
	 * @param string $userId ID of the user that faces belong to
57
	 * @param int $modelId ID of the model that faces belgon to
58
	 * @param int $fileId ID of file for which to search faces.
59
	 *
60
	 * @return array Array of faces on that file
61
	 */
62
	public function findFromFile(string $userId, int $modelId, int $fileId): array {
63
		$qb = $this->db->getQueryBuilder();
64
		$qb->select('f.id', 'left', 'right', 'top', 'bottom', 'person', 'confidence', 'creation_time')
65
			->from($this->getTableName(), 'f')
66
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
67
			->where($qb->expr()->eq('i.user', $qb->createParameter('user_id')))
68
			->andWhere($qb->expr()->eq('model', $qb->createParameter('model_id')))
69
			->andWhere($qb->expr()->eq('file', $qb->createParameter('file_id')))
70
			->setParameter('user_id', $userId)
71
			->setParameter('model_id', $modelId)
72
			->setParameter('file_id', $fileId)
73
			->orderBy('confidence', 'DESC');
74
		return $this->findEntities($qb);
75
	}
76
77
	/**
78
	 * Counts all the faces that belong to images of a given user, created using given model
79
	 *
80
	 * @param string $userId User to which faces and associated images belongs to
81
	 * @param int $model Model ID
82
	 * @param bool $onlyWithoutPersons True if we need to count only faces which are not having person associated for it.
83
	 * If false, all faces are counted.
84
	 */
85 14
	public function countFaces(string $userId, int $model, bool $onlyWithoutPersons=false): int {
86 14
		$qb = $this->db->getQueryBuilder();
87
		$qb = $qb
88 14
			->select($qb->createFunction('COUNT(' . $qb->getColumnName('f.id') . ')'))
89 14
			->from($this->getTableName(), 'f')
90 14
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
91 14
			->where($qb->expr()->eq('user', $qb->createParameter('user')))
92 14
			->andWhere($qb->expr()->eq('model', $qb->createParameter('model')));
93 14
		if ($onlyWithoutPersons) {
94
			$qb = $qb->andWhere($qb->expr()->isNull('person'));
95
		}
96
		$query = $qb
97 14
			->setParameter('user', $userId)
98 14
			->setParameter('model', $model);
99 14
		$resultStatement = $query->execute();
100 14
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
101 14
		$resultStatement->closeCursor();
102
103 14
		return (int)$data[0];
104
	}
105
106
	/**
107
	 * Gets oldest created face from database, for a given user and model, that is not associated with a person.
108
	 *
109
	 * @param string $userId User to which faces and associated images belongs to
110
	 * @param int $model Model ID
111
	 *
112
	 * @return Face Oldest face, if any is found
113
	 * @throws DoesNotExistException If there is no faces in database without person for a given user and model.
114
	 */
115
	public function getOldestCreatedFaceWithoutPerson(string $userId, int $model) {
116
		$qb = $this->db->getQueryBuilder();
117
		$qb
118
			->select('f.id', 'f.creation_time')
119
			->from($this->getTableName(), 'f')
120
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
121
			->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
122
			->andWhere($qb->expr()->eq('model', $qb->createNamedParameter($model)))
123
			->andWhere($qb->expr()->isNull('person'))
124
			->orderBy('f.creation_time', 'ASC');
125
		$cursor = $qb->execute();
126
		$row = $cursor->fetch();
127
		if($row === false) {
128
			$cursor->closeCursor();
129
			throw new DoesNotExistException("No faces found and we should have at least one");
130
		}
131
		$face = $this->mapRowToEntity($row);
132
		$cursor->closeCursor();
133
		return $face;
134
	}
135
136 17
	public function getFaces(string $userId, $model): array {
137 17
		$qb = $this->db->getQueryBuilder();
138 17
		$qb->select('f.id', 'f.person', 'f.left', 'f.right', 'f.top', 'f.bottom', 'f.confidence', 'f.descriptor')
139 17
			->from($this->getTableName(), 'f')
140 17
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
141 17
			->where($qb->expr()->eq('user', $qb->createParameter('user')))
142 17
			->andWhere($qb->expr()->eq('model', $qb->createParameter('model')))
143 17
			->setParameter('user', $userId)
144 17
			->setParameter('model', $model);
145 17
		return $this->findEntities($qb);
146
	}
147
148 13
	public function findFacesFromPerson(string $userId, int $personId, int $model, $limit = null, $offset = null): array {
149 13
		$qb = $this->db->getQueryBuilder();
150 13
		$qb->select('f.id', 'f.image', 'f.person')
151 13
			->from($this->getTableName(), 'f')
152 13
			->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id'))
153 13
			->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
154 13
			->andWhere($qb->expr()->eq('person', $qb->createNamedParameter($personId)))
155 13
			->andWhere($qb->expr()->eq('model', $qb->createNamedParameter($model)));
156
157 13
		$qb->setMaxResults($limit);
158 13
		$qb->setFirstResult($offset);
159
160 13
		$faces = $this->findEntities($qb);
161 13
		return $faces;
162
	}
163
164
	/**
165
	 * Finds all faces contained in one image
166
	 * Note that this is independent of any Model
167
	 *
168
	 * @param int $imageId Image for which to find all faces for
169
	 */
170
	public function findByImage(int $imageId) {
171
		$qb = $this->db->getQueryBuilder();
172
		$qb->select('id', 'image', 'person')
173
			->from($this->getTableName())
174
			->where($qb->expr()->eq('image', $qb->createNamedParameter($imageId)));
175
		$faces = $this->findEntities($qb);
176
		return $faces;
177
	}
178
179
	/**
180
	 * Removes all faces contained in one image.
181
	 * Note that this is independent of any Model
182
	 *
183
	 * @param int $imageId Image for which to delete faces for
184
	 */
185 2
	public function removeFromImage(int $imageId) {
186 2
		$qb = $this->db->getQueryBuilder();
187 2
		$qb->delete($this->getTableName())
188 2
			->where($qb->expr()->eq('image', $qb->createNamedParameter($imageId)))
189 2
			->execute();
190 2
	}
191
192
	/**
193
	 * Deletes all faces from that user.
194
	 *
195
	 * @param string $userId User to drop faces from table.
196
	 */
197 28
	public function deleteUserFaces(string $userId) {
198 28
		$sub = $this->db->getQueryBuilder();
199 28
		$sub->select(new Literal('1'));
200 28
		$sub->from('facerecog_images', 'i')
201 28
			->where($sub->expr()->eq('i.id', '*PREFIX*' . $this->getTableName() .'.image'))
202 28
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user')));
203
204 28
		$qb = $this->db->getQueryBuilder();
205 28
		$qb->delete($this->getTableName())
206 28
			->where('EXISTS (' . $sub->getSQL() . ')')
207 28
			->setParameter('user', $userId)
208 28
			->execute();
209 28
	}
210
211
	/**
212
	 * Deletes all faces from that user and model
213
	 *
214
	 * @param string $userId User to drop faces from table.
215
	 * @param int $modelId model to drop faces from table.
216
	 */
217
	public function deleteUserModel(string $userId, $modelId) {
218
		$sub = $this->db->getQueryBuilder();
219
		$sub->select(new Literal('1'));
220
		$sub->from('facerecog_images', 'i')
221
			->where($sub->expr()->eq('i.id', '*PREFIX*' . $this->getTableName() .'.image'))
222
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user')))
223
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model')));
224
225
		$qb = $this->db->getQueryBuilder();
226
		$qb->delete($this->getTableName())
227
			->where('EXISTS (' . $sub->getSQL() . ')')
228
			->setParameter('user', $userId)
229
			->setParameter('model', $modelId)
230
			->execute();
231
	}
232
233
	/**
234
	 * Unset relation beetwen faces and persons from that user in order to reset clustering
235
	 *
236
	 * @param string $userId User to drop fo unset relation.
237
	 */
238
	public function unsetPersonsRelationForUser(string $userId, int $model) {
239
		$sub = $this->db->getQueryBuilder();
240
		$sub->select(new Literal('1'));
241
		$sub->from('facerecog_images', 'i')
242
			->where($sub->expr()->eq('i.id', '*PREFIX*' . $this->getTableName() .'.image'))
243
			->andWhere($sub->expr()->eq('i.model', $sub->createParameter('model')))
244
			->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user')));
245
246
		$qb = $this->db->getQueryBuilder();
247
		$qb->update($this->getTableName())
248
			->set("person", $qb->createNamedParameter(null))
249
			->where('EXISTS (' . $sub->getSQL() . ')')
250
			->setParameter('model', $model)
251
			->setParameter('user', $userId)
252
			->execute();
253
	}
254
255
	/**
256
	 * Insert one face to database.
257
	 * Note: only reason we are not using (idiomatic) QBMapper method is
258
	 * because "QueryBuilder::PARAM_DATE" cannot be set there
259
	 *
260
	 * @param Face $face Face to insert
261
	 * @param IDBConnection $db Existing connection, if we need to reuse it. Null if we commit immediatelly.
262
	 */
263 15
	public function insertFace(Face $face, IDBConnection $db = null) {
264 15
		if ($db !== null) {
265 1
			$qb = $db->getQueryBuilder();
266
		} else {
267 14
			$qb = $this->db->getQueryBuilder();
268
		}
269
270 15
		$qb->insert($this->getTableName())
271 15
			->values([
272 15
				'image' => $qb->createNamedParameter($face->image),
273 15
				'person' => $qb->createNamedParameter($face->person),
274 15
				'left' => $qb->createNamedParameter($face->left),
275 15
				'right' => $qb->createNamedParameter($face->right),
276 15
				'top' => $qb->createNamedParameter($face->top),
277 15
				'bottom' => $qb->createNamedParameter($face->bottom),
278 15
				'confidence' => $qb->createNamedParameter($face->confidence),
279 15
				'landmarks' => $qb->createNamedParameter(json_encode($face->landmarks)),
280 15
				'descriptor' => $qb->createNamedParameter(json_encode($face->descriptor)),
281 15
				'creation_time' => $qb->createNamedParameter($face->creationTime, IQueryBuilder::PARAM_DATE),
282
			])
283 15
			->execute();
284
285 15
		$face->setId((int) $qb->getLastInsertId());
286
287 15
		return $face;
288
	}
289
}