@@ -55,10 +55,11 @@ class ValidationError(object):
55
55
a message and a rank which may be one of: 'error', 'warning'.
56
56
"""
57
57
58
- def __init__ (self , obj , msg , rank = LABEL_ERROR ):
58
+ def __init__ (self , obj , msg , rank = LABEL_ERROR , validation_id = None ):
59
59
self .obj = obj
60
60
self .msg = msg
61
61
self .rank = rank
62
+ self .validation_id = validation_id
62
63
63
64
@property
64
65
def is_warning (self ):
@@ -197,17 +198,18 @@ def object_required_attributes(obj):
197
198
198
199
:param obj: document, section or property.
199
200
"""
201
+ validation_id = ValidationID .object_required_attributes
200
202
201
203
args = obj .format ().arguments
202
204
for arg in args :
203
205
if arg [1 ] == 1 :
204
206
msg = "Missing required attribute '%s'" % (arg [0 ])
205
207
if not hasattr (obj , arg [0 ]):
206
- yield ValidationError (obj , msg , LABEL_ERROR )
208
+ yield ValidationError (obj , msg , LABEL_ERROR , validation_id )
207
209
continue
208
210
obj_arg = getattr (obj , arg [0 ])
209
211
if not obj_arg and not isinstance (obj_arg , bool ):
210
- yield ValidationError (obj , msg , LABEL_ERROR )
212
+ yield ValidationError (obj , msg , LABEL_ERROR , validation_id )
211
213
212
214
213
215
Validation .register_handler ('odML' , object_required_attributes )
@@ -221,8 +223,10 @@ def section_type_must_be_defined(sec):
221
223
222
224
:param sec: odml.Section.
223
225
"""
226
+ validation_id = ValidationID .section_type_must_be_defined
227
+
224
228
if sec .type and sec .type == "n.s." :
225
- yield ValidationError (sec , "Section type not specified" , LABEL_WARNING )
229
+ yield ValidationError (sec , "Section type not specified" , LABEL_WARNING , validation_id )
226
230
227
231
228
232
Validation .register_handler ('section' , section_type_must_be_defined )
@@ -235,22 +239,24 @@ def section_repository_present(sec):
235
239
1. warn, if a section has no repository or
236
240
2. the section type is not present in the repository
237
241
"""
242
+ validation_id = ValidationID .section_repository_present
243
+
238
244
repo = sec .get_repository ()
239
245
if repo is None :
240
246
msg = "A section should have an associated repository"
241
- yield ValidationError (sec , msg , LABEL_WARNING )
247
+ yield ValidationError (sec , msg , LABEL_WARNING , validation_id )
242
248
return
243
249
244
250
try :
245
251
tsec = sec .get_terminology_equivalent ()
246
252
except Exception as exc :
247
253
msg = "Could not load terminology: %s" % exc
248
- yield ValidationError (sec , msg , LABEL_WARNING )
254
+ yield ValidationError (sec , msg , LABEL_WARNING , validation_id )
249
255
return
250
256
251
257
if tsec is None :
252
258
msg = "Section type '%s' not found in terminology" % sec .type
253
- yield ValidationError (sec , msg , LABEL_WARNING )
259
+ yield ValidationError (sec , msg , LABEL_WARNING , validation_id )
254
260
255
261
256
262
def document_unique_ids (doc ):
@@ -280,6 +286,8 @@ def section_unique_ids(parent, id_map=None):
280
286
:param parent: odML Document or Section
281
287
:param id_map: "id":"odML object / path" dictionary
282
288
"""
289
+ validation_id = ValidationID .section_unique_ids
290
+
283
291
if not id_map :
284
292
id_map = {}
285
293
@@ -289,7 +297,7 @@ def section_unique_ids(parent, id_map=None):
289
297
290
298
if sec .id in id_map :
291
299
msg = "Duplicate id in Section '%s' and %s" % (sec .get_path (), id_map [sec .id ])
292
- yield ValidationError (sec , msg )
300
+ yield ValidationError (sec , msg , validation_id = validation_id )
293
301
else :
294
302
id_map [sec .id ] = "Section '%s'" % sec .get_path ()
295
303
@@ -309,39 +317,43 @@ def property_unique_ids(section, id_map=None):
309
317
:param section: odML Section
310
318
:param id_map: "id":"odML object / path" dictionary
311
319
"""
320
+ validation_id = ValidationID .property_unique_ids
321
+
312
322
if not id_map :
313
323
id_map = {}
314
324
315
325
for prop in section .properties :
316
326
if prop .id in id_map :
317
327
msg = "Duplicate id in Property '%s' and %s" % (prop .get_path (),
318
328
id_map [prop .id ])
319
- yield ValidationError (prop , msg )
329
+ yield ValidationError (prop , msg , validation_id = validation_id )
320
330
else :
321
331
id_map [prop .id ] = "Property '%s'" % prop .get_path ()
322
332
323
333
324
334
Validation .register_handler ('odML' , document_unique_ids )
325
335
326
336
327
- def object_unique_names (obj , children , attr = lambda x : x .name ,
337
+ def object_unique_names (obj , validation_id , children , attr = lambda x : x .name ,
328
338
msg = "Object names must be unique" ):
329
339
"""
330
- Tests that object names within one Section are unique.
340
+ Tests that object names within a Section are unique.
331
341
332
342
:param obj: odml class instance the validation is applied on.
343
+ :param validation_id: id of the
333
344
:param children: a function that returns the children to be considered.
334
- This is to be able to use the same function for sections and properties .
335
- :param attr: a function that returns the item that needs to be unique
336
- :param msg: error message that will be registered upon a ValidationError.
345
+ Required when handling Sections .
346
+ :param attr: a function that returns the attribute that needs to be unique.
347
+ :param msg: error message that will be registered with a ValidationError.
337
348
"""
338
349
names = set (map (attr , children (obj )))
339
350
if len (names ) == len (children (obj )):
340
- return # quick exit
351
+ return
352
+
341
353
names = set ()
342
354
for i in children (obj ):
343
355
if attr (i ) in names :
344
- yield ValidationError (i , msg , LABEL_ERROR )
356
+ yield ValidationError (i , msg , LABEL_ERROR , validation_id )
345
357
names .add (attr (i ))
346
358
347
359
@@ -353,6 +365,7 @@ def section_unique_name_type(obj):
353
365
"""
354
366
for i in object_unique_names (
355
367
obj ,
368
+ validation_id = ValidationID .section_unique_name_type ,
356
369
attr = lambda x : (x .name , x .type ),
357
370
children = lambda x : x .sections ,
358
371
msg = "name/type combination must be unique" ):
@@ -365,7 +378,9 @@ def property_unique_names(obj):
365
378
366
379
:param obj: odml class instance the validation is applied on.
367
380
"""
368
- for i in object_unique_names (obj , lambda x : x .properties ):
381
+ for i in object_unique_names (obj ,
382
+ validation_id = ValidationID .property_unique_name ,
383
+ children = lambda x : x .properties ):
369
384
yield i
370
385
371
386
@@ -380,8 +395,10 @@ def object_name_readable(obj):
380
395
381
396
:param obj: odml.Section or odml.Property.
382
397
"""
398
+ validation_id = ValidationID .object_name_readable
399
+
383
400
if obj .name == obj .id :
384
- yield ValidationError (obj , ' Name should be readable' , LABEL_WARNING )
401
+ yield ValidationError (obj , " Name should be readable" , LABEL_WARNING , validation_id )
385
402
386
403
387
404
Validation .register_handler ('section' , object_name_readable )
@@ -394,6 +411,8 @@ def property_terminology_check(prop):
394
411
2. warn, if there are multiple values with different units or the unit does
395
412
not match the one in the terminology.
396
413
"""
414
+ validation_id = ValidationID .property_terminology_check
415
+
397
416
if not prop .parent :
398
417
return
399
418
@@ -404,7 +423,7 @@ def property_terminology_check(prop):
404
423
tsec .properties [prop .name ]
405
424
except KeyError :
406
425
msg = "Property '%s' not found in terminology" % prop .name
407
- yield ValidationError (prop , msg , LABEL_WARNING )
426
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
408
427
409
428
410
429
Validation .register_handler ('property' , property_terminology_check )
@@ -415,6 +434,8 @@ def property_dependency_check(prop):
415
434
Produces a warning if the dependency attribute refers to a non-existent attribute
416
435
or the dependency_value does not match.
417
436
"""
437
+ validation_id = ValidationID .property_dependency_check
438
+
418
439
if not prop .parent :
419
440
return
420
441
@@ -426,12 +447,12 @@ def property_dependency_check(prop):
426
447
dep_obj = prop .parent [dep ]
427
448
except KeyError :
428
449
msg = "Property refers to a non-existent dependency object"
429
- yield ValidationError (prop , msg , LABEL_WARNING )
450
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
430
451
return
431
452
432
453
if prop .dependency_value not in dep_obj .values [0 ]:
433
454
msg = "Dependency-value is not equal to value of the property's dependency"
434
- yield ValidationError (prop , msg , LABEL_WARNING )
455
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
435
456
436
457
437
458
Validation .register_handler ('property' , property_dependency_check )
@@ -444,6 +465,7 @@ def property_values_check(prop):
444
465
445
466
:param prop: property the validation is applied on.
446
467
"""
468
+ validation_id = ValidationID .property_values_check
447
469
448
470
if prop .dtype is not None and prop .dtype != "" :
449
471
dtype = prop .dtype
@@ -457,13 +479,13 @@ def property_values_check(prop):
457
479
tuple_len = int (dtype [:- 6 ])
458
480
if len (val ) != tuple_len :
459
481
msg = "Tuple of length %s not consistent with dtype %s!" % (len (val ), dtype )
460
- yield ValidationError (prop , msg , LABEL_WARNING )
482
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
461
483
else :
462
484
try :
463
485
dtypes .get (val , dtype )
464
486
except ValueError :
465
487
msg = "Property values not of consistent dtype!"
466
- yield ValidationError (prop , msg , LABEL_WARNING )
488
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
467
489
468
490
469
491
Validation .register_handler ('property' , property_values_check )
@@ -477,6 +499,7 @@ def property_values_string_check(prop):
477
499
478
500
:param prop: property the validation is applied on.
479
501
"""
502
+ validation_id = ValidationID .property_values_string_check
480
503
481
504
if prop .dtype != "string" or not prop .values :
482
505
return
@@ -515,14 +538,15 @@ def property_values_string_check(prop):
515
538
res_dtype = "string"
516
539
517
540
if res_dtype != "string" :
518
- msg = 'Dtype of property "%s" currently is "string", but might fit dtype "%s"!' % (prop .name , res_dtype )
519
- yield ValidationError (prop , msg , LABEL_WARNING )
541
+ msg = 'Dtype of property "%s" currently is "string", but might fit dtype "%s"!' % \
542
+ (prop .name , res_dtype )
543
+ yield ValidationError (prop , msg , LABEL_WARNING , validation_id )
520
544
521
545
522
546
Validation .register_handler ('property' , property_values_string_check )
523
547
524
548
525
- def _cardinality_validation (obj , cardinality , card_target_attr , validation_rank ):
549
+ def _cardinality_validation (obj , cardinality , card_target_attr , validation_rank , validation_id ):
526
550
"""
527
551
Helper function that validates the cardinality of an odml object attribute.
528
552
Valid object-attribute combinations are Section-sections, Section-properties and
@@ -534,6 +558,7 @@ def _cardinality_validation(obj, cardinality, card_target_attr, validation_rank)
534
558
applied against. Supported values are:
535
559
'sections', 'properties' or 'values'
536
560
:param validation_rank: Rank of the yielded ValidationError.
561
+ :param validation_id: string containing the id of the parent validation.
537
562
538
563
:return: Returns a ValidationError, if a set cardinality is not met or None.
539
564
"""
@@ -557,7 +582,7 @@ def _cardinality_validation(obj, cardinality, card_target_attr, validation_rank)
557
582
msg = "%s %s cardinality violated" % (obj_name , card_target_attr )
558
583
msg += " (%s values, %s found)" % (invalid_cause , val_len )
559
584
560
- err = ValidationError (obj , msg , validation_rank )
585
+ err = ValidationError (obj , msg , validation_rank , validation_id )
561
586
562
587
return err
563
588
@@ -570,7 +595,10 @@ def section_properties_cardinality(obj):
570
595
571
596
:return: Yields a ValidationError warning, if a set cardinality is not met.
572
597
"""
573
- err = _cardinality_validation (obj , obj .prop_cardinality , 'properties' , LABEL_WARNING )
598
+ validation_id = ValidationID .section_properties_cardinality
599
+
600
+ err = _cardinality_validation (obj , obj .prop_cardinality , 'properties' ,
601
+ LABEL_WARNING , validation_id )
574
602
if err :
575
603
yield err
576
604
@@ -586,7 +614,10 @@ def section_sections_cardinality(obj):
586
614
587
615
:return: Yields a ValidationError warning, if a set cardinality is not met.
588
616
"""
589
- err = _cardinality_validation (obj , obj .sec_cardinality , 'sections' , LABEL_WARNING )
617
+ validation_id = ValidationID .section_sections_cardinality
618
+
619
+ err = _cardinality_validation (obj , obj .sec_cardinality , 'sections' ,
620
+ LABEL_WARNING , validation_id )
590
621
if err :
591
622
yield err
592
623
@@ -602,7 +633,10 @@ def property_values_cardinality(obj):
602
633
603
634
:return: Yields a ValidationError warning, if a set cardinality is not met.
604
635
"""
605
- err = _cardinality_validation (obj , obj .val_cardinality , 'values' , LABEL_WARNING )
636
+ validation_id = ValidationID .property_values_cardinality
637
+
638
+ err = _cardinality_validation (obj , obj .val_cardinality , 'values' ,
639
+ LABEL_WARNING , validation_id )
606
640
if err :
607
641
yield err
608
642
0 commit comments