@@ -42,6 +42,14 @@ class InvalidChangesetError(Exception):
42
42
pass
43
43
44
44
45
+ class Reason :
46
+ """Details and meta-data for a suspicion reason."""
47
+ def __init__ (self , identifier , label = None , source = "osmcha" ):
48
+ self .identifier = identifier
49
+ self .label = label if label else identifier
50
+ self .source = source
51
+
52
+
45
53
def get_user_details (user_id ):
46
54
"""Get information about number of changesets, blocks and mapping days of a
47
55
user, using both the OSM API and the Mapbox comments APIself.
@@ -56,7 +64,7 @@ def get_user_details(user_id):
56
64
changesets = [i for i in xml_data if i .tag == 'changesets' ][0 ]
57
65
blocks = [i for i in xml_data if i .tag == 'blocks' ][0 ]
58
66
if int (changesets .get ('count' )) <= 5 :
59
- reasons .append ('New mapper' )
67
+ reasons .append (Reason ( 'New mapper' ) )
60
68
elif int (changesets .get ('count' )) <= 30 :
61
69
url = MAPBOX_USERS_API .format (
62
70
user_id = requests .compat .quote (user_id )
@@ -67,9 +75,9 @@ def get_user_details(user_id):
67
75
user_request .json ().get ('extra' ).get ('mapping_days' )
68
76
)
69
77
if mapping_days <= 5 :
70
- reasons .append ('New mapper' )
78
+ reasons .append (Reason ( 'New mapper' ) )
71
79
if int (blocks [0 ].get ('count' )) > 1 :
72
- reasons .append ('User has multiple blocks' )
80
+ reasons .append (Reason ( 'User has multiple blocks' ) )
73
81
except Exception as e :
74
82
message = 'Could not verify user of the changeset: {}, {}'
75
83
print (message .format (user_id , str (e )))
@@ -239,7 +247,6 @@ def filter(self):
239
247
if get_bounds (ch ).intersects (self .area )
240
248
]
241
249
242
-
243
250
class Analyse (object ):
244
251
"""Analyse a changeset and define if it is suspect."""
245
252
def __init__ (self , changeset , create_threshold = 200 , modify_threshold = 200 ,
@@ -299,6 +306,7 @@ def set_fields(self, changeset):
299
306
'%Y-%m-%dT%H:%M:%SZ'
300
307
)
301
308
self .suspicion_reasons = []
309
+ self .detailed_reasons = {}
302
310
self .is_suspect = False
303
311
self .powerfull_editor = False
304
312
self .warning_tags = [
@@ -307,7 +315,8 @@ def set_fields(self, changeset):
307
315
308
316
def label_suspicious (self , reason ):
309
317
"""Add suspicion reason and set the suspicious flag."""
310
- self .suspicion_reasons .append (reason )
318
+ self .suspicion_reasons .append (reason .identifier )
319
+ self .detailed_reasons [reason .identifier ] = reason
311
320
self .is_suspect = True
312
321
313
322
def full_analysis (self ):
@@ -318,12 +327,12 @@ def full_analysis(self):
318
327
self .verify_warning_tags ()
319
328
320
329
if self .review_requested == 'yes' :
321
- self .label_suspicious ('Review requested' )
330
+ self .label_suspicious (Reason ( 'Review requested' ) )
322
331
323
332
def verify_warning_tags (self ):
324
333
for tag in self .warning_tags :
325
334
if tag in self .enabled_warnings .keys ():
326
- self .label_suspicious (self .enabled_warnings .get (tag ))
335
+ self .label_suspicious (Reason ( self .enabled_warnings .get (tag ), None , self . editor ))
327
336
328
337
def verify_user (self ):
329
338
"""Verify if the changeset was made by a inexperienced mapper (anyone
@@ -338,21 +347,21 @@ def verify_words(self):
338
347
"""
339
348
if self .comment :
340
349
if find_words (self .comment , self .suspect_words , self .excluded_words ):
341
- self .label_suspicious ('suspect_word' )
350
+ self .label_suspicious (Reason ( 'suspect_word' ) )
342
351
343
352
if self .source :
344
353
for word in self .illegal_sources :
345
354
if word in self .source .lower ():
346
355
if word == 'yandex' and 'yandex panorama' in self .source .lower ():
347
356
pass
348
357
else :
349
- self .label_suspicious ('suspect_word' )
358
+ self .label_suspicious (Reason ( 'suspect_word' ) )
350
359
break
351
360
352
361
if self .imagery_used :
353
362
for word in self .illegal_sources :
354
363
if word in self .imagery_used .lower ():
355
- self .label_suspicious ('suspect_word' )
364
+ self .label_suspicious (Reason ( 'suspect_word' ) )
356
365
break
357
366
358
367
self .suspicion_reasons = list (set (self .suspicion_reasons ))
@@ -383,10 +392,10 @@ def verify_editor(self):
383
392
'mapwith.ai'
384
393
]
385
394
if self .host .split ('://' )[- 1 ].split ('/' )[0 ] not in trusted_hosts :
386
- self .label_suspicious ('Unknown iD instance' )
395
+ self .label_suspicious (Reason ( 'Unknown iD instance' ) )
387
396
else :
388
397
self .powerfull_editor = True
389
- self .label_suspicious ('Software editor was not declared' )
398
+ self .label_suspicious (Reason ( 'Software editor was not declared' ) )
390
399
391
400
def count (self ):
392
401
"""Count the number of elements created, modified and deleted by the
@@ -404,14 +413,14 @@ def count(self):
404
413
if (self .create / len (actions ) > self .percentage and
405
414
self .create > self .create_threshold and
406
415
(self .powerfull_editor or self .create > self .top_threshold )):
407
- self .label_suspicious ('possible import' )
416
+ self .label_suspicious (Reason ( 'possible import' ) )
408
417
elif (self .modify / len (actions ) > self .percentage and
409
418
self .modify > self .modify_threshold ):
410
- self .label_suspicious ('mass modification' )
419
+ self .label_suspicious (Reason ( 'mass modification' ) )
411
420
elif ((self .delete / len (actions ) > self .percentage and
412
421
self .delete > self .delete_threshold ) or
413
422
self .delete > self .top_threshold ):
414
- self .label_suspicious ('mass deletion' )
423
+ self .label_suspicious (Reason ( 'mass deletion' ) )
415
424
except ZeroDivisionError :
416
425
print ('It seems this changeset was redacted' )
417
426
0 commit comments