Passed
Push — test-scrutinizer-new-analyser ( 748b29 )
by Branko
05:41
created

Watcher::postWrite()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 3
nop 1
dl 0
loc 28
ccs 0
cts 19
cp 0
crap 20
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, Roeland Jago Douma <[email protected]>
4
 * @copyright Copyright (c) 2017, 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\IHomeStorage;
29
use OCP\Files\Node;
30
use OCP\IConfig;
31
use OCP\IDBConnection;
32
use OCP\ILogger;
33
use OCP\IUserManager;
34
35
use OCA\FaceRecognition\BackgroundJob\Tasks\AddMissingImagesTask;
36
use OCA\FaceRecognition\Db\Face;
37
use OCA\FaceRecognition\Db\Image;
38
use OCA\FaceRecognition\Db\FaceMapper;
39
use OCA\FaceRecognition\Db\FaceNewMapper;
40
use OCA\FaceRecognition\Db\ImageMapper;
41
use OCA\FaceRecognition\Db\PersonMapper;
42
use OCA\FaceRecognition\Helper\Requirements;
43
use OCA\FaceRecognition\Migration\AddDefaultFaceModel;
44
45
class Watcher {
46
47
	/** @var IConfig Config */
48
	private $config;
49
50
	/** @var ILogger Logger */
51
	private $logger;
52
53
	/** @var IDBConnection */
54
	private $connection;
55
56
	/** @var IUserManager */
57
	private $userManager;
58
59
	/** @var FaceMapper */
60
	private $faceMapper;
61
62
	/** @var FaceNewMapper */
63
	private $faceNewMapper;
64
65
	/** @var ImageMapper */
66
	private $imageMapper;
67
68
	/** @var PersonMapper */
69
	private $personMapper;
70
71
	/**
72
	 * Watcher constructor.
73
	 *
74
	 * @param IConfig $config
75
	 * @param ILogger $logger
76
	 * @param IDBConnection $connection
77
	 * @param IUserManager $userManager
78
	 * @param FaceMapper $faceMapper
79
	 * @param FaceNewMapper $faceNewMapper
80
	 * @param ImageMapper $imageMapper
81
	 * @param PersonMapper $personMapper
82
	 */
83
	public function __construct(IConfig       $config,
84
								ILogger       $logger,
85
								IDBConnection $connection,
86
								IUserManager  $userManager,
87
								FaceMapper    $faceMapper,
88
								FaceNewMapper $faceNewMapper,
89
								ImageMapper   $imageMapper,
90
								PersonMapper  $personMapper) {
91
		$this->config = $config;
92
		$this->logger = $logger;
93
		$this->connection = $connection;
94
		$this->userManager = $userManager;
95
		$this->faceMapper = $faceMapper;
96
		$this->faceNewMapper = $faceNewMapper;
97
		$this->imageMapper = $imageMapper;
98
		$this->personMapper = $personMapper;
99
	}
100
101
	/**
102
	 * A node has been updated. We just store the file id
103
	 * with the current user in the DB
104
	 *
105
	 * @param Node $node
106
	 */
107
	public function postWrite(Node $node) {
108
		// v1 code
109
		$absPath = ltrim($node->getPath(), '/');
110
		$owner = explode('/', $absPath)[0];
111
112
		if (!$this->userManager->userExists($owner) || $node instanceof Folder) {
113
			return;
114
		}
115
116
117
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
118
			return;
119
		}
120
121
//		if ($this->faceMapper->fileExists($node->getId())) {
122
//			return;
123
//		}
124
125
		$face = new Face();
126
		$face->setUid($owner);
0 ignored issues
show
Bug introduced by
The method setUid() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

126
		$face->/** @scrutinizer ignore-call */ 
127
         setUid($owner);
Loading history...
127
		$face->setFile($node->getId());
0 ignored issues
show
Bug introduced by
The method setFile() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

127
		$face->/** @scrutinizer ignore-call */ 
128
         setFile($node->getId());
Loading history...
128
		$face->setName('unknown');
0 ignored issues
show
Bug introduced by
The method setName() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

128
		$face->/** @scrutinizer ignore-call */ 
129
         setName('unknown');
Loading history...
129
		$face->setDistance(-1.0);
0 ignored issues
show
Bug introduced by
The method setDistance() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

129
		$face->/** @scrutinizer ignore-call */ 
130
         setDistance(-1.0);
Loading history...
130
		$face->setTop(-1.0);
0 ignored issues
show
Bug introduced by
The method setTop() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

130
		$face->/** @scrutinizer ignore-call */ 
131
         setTop(-1.0);
Loading history...
131
		$face->setRight(-1.0);
0 ignored issues
show
Bug introduced by
The method setRight() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

131
		$face->/** @scrutinizer ignore-call */ 
132
         setRight(-1.0);
Loading history...
132
		$face->setBottom(-1.0);
0 ignored issues
show
Bug introduced by
The method setBottom() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

132
		$face->/** @scrutinizer ignore-call */ 
133
         setBottom(-1.0);
Loading history...
133
		$face->setLeft(-1.0);
0 ignored issues
show
Bug introduced by
The method setLeft() does not exist on OCA\FaceRecognition\Db\Face. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

133
		$face->/** @scrutinizer ignore-call */ 
134
         setLeft(-1.0);
Loading history...
134
		$this->faceMapper->insert($face);
135
136
	}
137
138
	/**
139
	 * @param Node $node
140
	 */
141
	public function postWritev2(Node $node) {
142
		$model = intval($this->config->getAppValue('facerecognition', 'model', AddDefaultFaceModel::DEFAULT_FACE_MODEL_ID));
143
144
		// todo: should we also care about this too: instanceOfStorage(ISharedStorage::class);
145
		if ($node->getStorage()->instanceOfStorage(IHomeStorage::class) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
146
			return;
147
		}
148
149
		if ($node instanceof Folder) {
150
			return;
151
		}
152
153
		// todo: issue #37 - if we detect .nomedia written, reset some global flag such that RemoveMissingImagesTask is triggered.
154
155
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
156
			return;
157
		}
158
159
		// If we detect .nomedia file anywhere on the path to root folder (id==null), bail out
160
		$parentNode = $node->getParent();
161
		while (($parentNode instanceof Folder) && ($parentNode->getId() != null)) {
162
			if ($parentNode->nodeExists('.nomedia')) {
163
				$this->logger->debug(
164
					"Skipping inserting image " . $node->getName() . " because directory " . $parentNode->getName() . " contains .nomedia file");
165
				return;
166
			}
167
168
			$parentNode = $parentNode->getParent();
169
		}
170
171
		$owner = $node->getOwner()->getUid();
172
173
		if (!$this->userManager->userExists($owner)) {
174
			$this->logger->debug(
175
				"Skipping inserting image " . $node->getName() . " because it seems that user  " . $owner . " doesn't exist");
176
			return;
177
		}
178
179
		$this->logger->debug("Inserting/updating image " . $node->getName() . " for face recognition");
180
181
		$image = new Image();
182
		$image->setUser($owner);
183
		$image->setFile($node->getId());
184
		$image->setModel($model);
185
186
		$imageId = $this->imageMapper->imageExists($image);
187
		if ($imageId === null) {
188
			// todo: can we have larger transaction with bulk insert?
189
			$this->imageMapper->insert($image);
190
		} else {
191
			$this->imageMapper->resetImage($image);
192
			// note that invalidatePersons depends on existence of faces for a given image,
193
			// and we must invalidate before we delete faces!
194
			$this->personMapper->invalidatePersons($imageId);
195
			$this->faceNewMapper->removeFaces($imageId);
196
		}
197
	}
198
199
	/**
200
	 * A node has been delete. Remove faces with file id
201
	 * with the current user in the DB
202
	 *
203
	 * @param Node $node
204
	 */
205
	public function preDelete(Node $node) {
206
		$absPath = ltrim($node->getPath(), '/');
207
		$owner = explode('/', $absPath)[0];
208
209
		if (!$this->userManager->userExists($owner) || $node instanceof Folder) {
210
			return;
211
		}
212
213
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
214
			return;
215
		}
216
217
		try {
218
			$faces = $this->faceMapper->findFile($owner, $node->getId());
219
		} catch(\Exception $e) {
220
			return;
221
		}
222
223
		foreach ($faces as $face) {
224
			$this->faceMapper->delete($face);
225
		}
226
	}
227
228
	public function postDeletev2(Node $node) {
229
		$model = intval($this->config->getAppValue('facerecognition', 'model', AddDefaultFaceModel::DEFAULT_FACE_MODEL_ID));
230
231
		// todo: should we also care about this too: instanceOfStorage(ISharedStorage::class);
232
		if ($node->getStorage()->instanceOfStorage(IHomeStorage::class) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
233
			return;
234
		}
235
236
		if ($node instanceof Folder) {
237
			return;
238
		}
239
240
		if ($node->getName() == '.nomedia') {
241
			// If user deleted file named .nomedia, that means all images in this and all child directories should be added.
242
			// But, instead of doing that here, better option seem to be to just reset global flag that image scan is not done.
243
			// This will trigger another round of image crawling in AddMissingImagesTask and those images will be added.
244
			$this->config->setAppValue('facerecognition', AddMissingImagesTask::FULL_IMAGE_SCAN_DONE_KEY, 'false');
245
			return;
246
		}
247
248
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
249
			return;
250
		}
251
252
		$owner = $node->getOwner()->getUid();
253
254
		$this->logger->debug("Deleting image " . $node->getName() . " from face recognition");
255
256
		$image = new Image();
257
		$image->setUser($owner);
258
		$image->setFile($node->getId());
259
		$image->setModel($model);
260
261
		$imageId = $this->imageMapper->imageExists($image);
262
		if ($imageId !== null) {
263
			// note that invalidatePersons depends on existence of faces for a given image,
264
			// and we must invalidate before we delete faces!
265
			$this->personMapper->invalidatePersons($imageId);
266
			$this->faceNewMapper->removeFaces($imageId);
267
268
			$image->setId($imageId);
269
			$this->imageMapper->delete($image);
270
		}
271
	}
272
}
273