Passed
Push — dependabot/npm_and_yarn/ellipt... ( eb0fee )
by
unknown
24:49
created

Watcher::postDelete()   C

Complexity

Conditions 13
Paths 20

Size

Total Lines 77
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 14
Bugs 1 Features 0
Metric Value
cc 13
eloc 44
c 14
b 1
f 0
nc 20
nop 1
dl 0
loc 77
ccs 0
cts 44
cp 0
crap 182
rs 6.6166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, Roeland Jago Douma <[email protected]>
4
 * @copyright Copyright (c) 2017-2020 Matias De lellis <[email protected]>
5
 *
6
 * @author Roeland Jago Douma <[email protected]>
7
 * @author Matias De lellis <[email protected]>
8
 *
9
 * @license GNU AGPL version 3 or any later version
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License as
13
 * published by the Free Software Foundation, either version 3 of the
14
 * License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
 *
24
 */
25
namespace OCA\FaceRecognition;
26
27
use OCP\Files\Folder;
28
use OCP\Files\Node;
29
use OCP\ILogger;
30
use OCP\IUserManager;
31
32
use OCA\FaceRecognition\Service\FaceManagementService;
33
use OCA\FaceRecognition\Service\FileService;
34
use OCA\FaceRecognition\Service\SettingsService;
35
36
use OCA\FaceRecognition\Db\Face;
37
use OCA\FaceRecognition\Db\Image;
38
39
use OCA\FaceRecognition\Db\FaceMapper;
40
use OCA\FaceRecognition\Db\ImageMapper;
41
use OCA\FaceRecognition\Db\PersonMapper;
42
43
class Watcher {
44
45
	/** @var ILogger Logger */
46
	private $logger;
47
48
	/** @var IUserManager */
49
	private $userManager;
50
51
	/** @var FaceMapper */
52
	private $faceMapper;
53
54
	/** @var ImageMapper */
55
	private $imageMapper;
56
57
	/** @var PersonMapper */
58
	private $personMapper;
59
60
	/** @var SettingsService */
61
	private $settingsService;
62
63
	/** @var FileService */
64
	private $fileService;
65
66
	/** @var FaceManagementService */
67
	private $faceManagementService;
68
69
	/**
70
	 * Watcher constructor.
71
	 *
72
	 * @param ILogger $logger
73
	 * @param IUserManager $userManager
74
	 * @param FaceMapper $faceMapper
75
	 * @param ImageMapper $imageMapper
76
	 * @param PersonMapper $personMapper
77
	 * @param SettingsService $settingsService
78
	 * @param FileService $fileService
79
	 * @param FaceManagementService $faceManagementService
80
	 */
81 1
	public function __construct(ILogger               $logger,
82
	                            IUserManager          $userManager,
83
	                            FaceMapper            $faceMapper,
84
	                            ImageMapper           $imageMapper,
85
	                            PersonMapper          $personMapper,
86
	                            SettingsService       $settingsService,
87
	                            FileService           $fileService,
88
	                            FaceManagementService $faceManagementService)
89
	{
90 1
		$this->logger                = $logger;
91 1
		$this->userManager           = $userManager;
92 1
		$this->faceMapper            = $faceMapper;
93 1
		$this->imageMapper           = $imageMapper;
94 1
		$this->personMapper          = $personMapper;
95 1
		$this->settingsService       = $settingsService;
96 1
		$this->fileService           = $fileService;
97 1
		$this->faceManagementService = $faceManagementService;
98 1
	}
99
100
	/**
101
	 * A node has been updated. We just store the file id
102
	 * with the current user in the DB
103
	 *
104
	 * @param Node $node
105
	 */
106 28
	public function postWrite(Node $node) {
107 28
		if (!$this->fileService->isAllowedNode($node)) {
108
			// Nextcloud sends the Hooks when create thumbnails for example.
109 28
			return;
110
		}
111
112 28
		if ($node instanceof Folder) {
113 28
			return;
114
		}
115
116
		$modelId = $this->settingsService->getCurrentFaceModel();
117
		if ($modelId === SettingsService::FALLBACK_CURRENT_MODEL) {
118
			$this->logger->debug("Skipping inserting file since there are no configured model");
119
			return;
120
		}
121
122
		$owner = null;
123
		if ($this->fileService->isUserFile($node)) {
124
			$owner = $node->getOwner()->getUid();
125
		} else {
126
			if (!\OC::$server->getUserSession()->isLoggedIn()) {
127
				$this->logger->debug('Skipping interting file ' . $node->getName() . ' since we cannot determine the owner.');
128
				return;
129
			}
130
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
131
		}
132
133
		if (!$this->userManager->userExists($owner)) {
134
			$this->logger->debug(
135
				"Skipping inserting file " . $node->getName() . " because it seems that user  " . $owner . " doesn't exist");
136
			return;
137
		}
138
139
		$enabled = $this->settingsService->getUserEnabled($owner);
140
		if (!$enabled) {
141
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
142
			return;
143
		}
144
145
		if ($node->getName() === FileService::NOMEDIA_FILE) {
146
			// If user added this file, it means all images in this and all child directories should be removed.
147
			// Instead of doing that here, it's better to just add flag that image removal should be done.
148
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
149
			return;
150
		}
151
152
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
153
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
154
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
155
			$this->settingsService->setUserFullScanDone(false, $owner);
156
			return;
157
		}
158
159
		if (!$this->settingsService->isAllowedMimetype($node->getMimeType())) {
160
			// The file is not an image or the model does not support it
161
			return;
162
		}
163
164
		if ($this->fileService->isUnderNoDetection($node)) {
165
			$this->logger->debug(
166
				"Skipping inserting image " . $node->getName() . " because is inside an folder that contains a .nomedia file");
167
			return;
168
		}
169
170
		$this->logger->debug("Inserting/updating image " . $node->getName() . " for face recognition");
171
172
		$image = new Image();
173
		$image->setUser($owner);
174
		$image->setFile($node->getId());
175
		$image->setModel($modelId);
176
177
		$imageId = $this->imageMapper->imageExists($image);
178
		if ($imageId === null) {
179
			// todo: can we have larger transaction with bulk insert?
180
			$this->imageMapper->insert($image);
181
		} else {
182
			$this->imageMapper->resetImage($image);
183
			// note that invalidatePersons depends on existence of faces for a given image,
184
			// and we must invalidate before we delete faces!
185
			$this->personMapper->invalidatePersons($imageId);
186
187
			// Fetch all faces to be deleted before deleting them, and then delete them
188
			$facesToRemove = $this->faceMapper->findByImage($imageId);
189
			$this->faceMapper->removeFromImage($imageId);
190
191
			// If any person is now without faces, remove those (empty) persons
192
			foreach ($facesToRemove as $faceToRemove) {
193
				if ($faceToRemove->getPerson() !== null) {
194
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
195
				}
196
			}
197
		}
198
	}
199
200
	/**
201
	 * A node has been deleted. Remove faces with file id
202
	 * with the current user in the DB
203
	 *
204
	 * @param Node $node
205
	 */
206
	public function postDelete(Node $node) {
207
		if (!$this->fileService->isAllowedNode($node)) {
208
			// Nextcloud sends the Hooks when create thumbnails for example.
209
			return;
210
		}
211
212
		if ($node instanceof Folder) {
213
			return;
214
		}
215
216
		$modelId = $this->settingsService->getCurrentFaceModel();
217
		if ($modelId === SettingsService::FALLBACK_CURRENT_MODEL) {
218
			$this->logger->debug("Skipping deleting file since there are no configured model");
219
			return;
220
		}
221
222
		$owner = null;
223
		if ($this->fileService->isUserFile($node)) {
224
			$owner = $node->getOwner()->getUid();
225
		} else {
226
			if (!\OC::$server->getUserSession()->isLoggedIn()) {
227
				$this->logger->debug('Skipping deleting the file ' . $node->getName() .  ' since we cannot determine the owner');
228
				return;
229
			}
230
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
231
		}
232
233
		$enabled = $this->settingsService->getUserEnabled($owner);
234
		if (!$enabled) {
235
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
236
			return;
237
		}
238
239
		if ($node->getName() === FileService::NOMEDIA_FILE) {
240
			// If user deleted file named .nomedia, that means all images in this and all child directories should be added.
241
			// But, instead of doing that here, better option seem to be to just reset flag that image scan is not done.
242
			// This will trigger another round of image crawling in AddMissingImagesTask for this user and those images will be added.
243
			$this->settingsService->setUserFullScanDone(false, $owner);
244
			return;
245
		}
246
247
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
248
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
249
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
250
			$this->settingsService->setUserFullScanDone(false, $owner);
251
			return;
252
		}
253
254
		if (!$this->settingsService->isAllowedMimetype($node->getMimeType())) {
255
			// The file is not an image or the model does not support it
256
			return;
257
		}
258
259
		$this->logger->debug("Deleting image " . $node->getName() . " from face recognition");
260
261
		$image = new Image();
262
		$image->setUser($owner);
263
		$image->setFile($node->getId());
264
		$image->setModel($modelId);
265
266
		$imageId = $this->imageMapper->imageExists($image);
267
		if ($imageId !== null) {
268
			// note that invalidatePersons depends on existence of faces for a given image,
269
			// and we must invalidate before we delete faces!
270
			$this->personMapper->invalidatePersons($imageId);
271
272
			// Fetch all faces to be deleted before deleting them, and then delete them
273
			$facesToRemove = $this->faceMapper->findByImage($imageId);
274
			$this->faceMapper->removeFromImage($imageId);
275
276
			$image->setId($imageId);
277
			$this->imageMapper->delete($image);
278
279
			// If any person is now without faces, remove those (empty) persons
280
			foreach ($facesToRemove as $faceToRemove) {
281
				if ($faceToRemove->getPerson() !== null) {
282
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
283
				}
284
			}
285
		}
286
	}
287
288
	/**
289
	 * A user has been deleted. Cleanup everything from this user.
290
	 *
291
	 * @param \OC\User\User $user Deleted user
292
	 */
293 28
	public function postUserDelete(\OC\User\User $user) {
0 ignored issues
show
Bug introduced by
The type OC\User\User 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...
294 28
		$userId = $user->getUid();
295 28
		$this->faceManagementService->resetAllForUser($userId);
296 28
		$this->logger->info("Removed all face recognition data for deleted user " . $userId);
297 28
	}
298
}
299