@@ -82,6 +82,10 @@ class BaseProperty(base.BaseObject):
82
82
:param oid: object id, UUID string as specified in RFC 4122. If no id is provided,
83
83
an id will be generated and assigned. An id has to be unique
84
84
within an odML Document.
85
+ :param val_cardinality: Value cardinality defines how many values are allowed for this Property.
86
+ By default unlimited values can be set.
87
+ A required number of values can be set by assigning a tuple of the
88
+ format "(min, max)".
85
89
:param value: Legacy code to the 'values' attribute. If 'values' is provided,
86
90
any data provided via 'value' will be ignored.
87
91
"""
@@ -91,7 +95,7 @@ class BaseProperty(base.BaseObject):
91
95
def __init__ (self , name = None , values = None , parent = None , unit = None ,
92
96
uncertainty = None , reference = None , definition = None ,
93
97
dependency = None , dependency_value = None , dtype = None ,
94
- value_origin = None , oid = None , value = None ):
98
+ value_origin = None , oid = None , val_cardinality = None , value = None ):
95
99
96
100
try :
97
101
if oid is not None :
@@ -115,6 +119,7 @@ def __init__(self, name=None, values=None, parent=None, unit=None,
115
119
self ._definition = definition
116
120
self ._dependency = dependency
117
121
self ._dependency_value = dependency_value
122
+ self ._val_cardinality = None
118
123
119
124
self ._dtype = None
120
125
if dtypes .valid_type (dtype ):
@@ -129,6 +134,10 @@ def __init__(self, name=None, values=None, parent=None, unit=None,
129
134
130
135
self .parent = parent
131
136
137
+ # Cardinality should always be set after values have been added
138
+ # since it is always tested against values when it is set.
139
+ self .val_cardinality = val_cardinality
140
+
132
141
for err in validation .Validation (self ).errors :
133
142
if err .is_error :
134
143
msg = "\n \t - %s %s: %s" % (err .obj , err .rank , err .msg )
@@ -401,6 +410,11 @@ def values(self, new_value):
401
410
raise ValueError (msg )
402
411
self ._values = [dtypes .get (v , self .dtype ) for v in new_value ]
403
412
413
+ # Validate and inform user if the current values cardinality is violated
414
+ valid = validation .Validation (self )
415
+ for err in valid .errors :
416
+ print ("%s: %s" % (err .rank .capitalize (), err .msg ))
417
+
404
418
@property
405
419
def value_origin (self ):
406
420
"""
@@ -507,6 +521,88 @@ def dependency_value(self, new_value):
507
521
new_value = None
508
522
self ._dependency_value = new_value
509
523
524
+ @property
525
+ def val_cardinality (self ):
526
+ """
527
+ The value cardinality of a Property. It defines how many values
528
+ are minimally required and how many values should be maximally
529
+ stored. Use 'values_set_cardinality' to set.
530
+ """
531
+ return self ._val_cardinality
532
+
533
+ @val_cardinality .setter
534
+ def val_cardinality (self , new_value ):
535
+ """
536
+ Sets the values cardinality of a Property.
537
+
538
+ The following cardinality cases are supported:
539
+ (n, n) - default, no restriction
540
+ (d, n) - minimally d entries, no maximum
541
+ (n, d) - maximally d entries, no minimum
542
+ (d, d) - minimally d entries, maximally d entries
543
+
544
+ Only positive integers are supported. 'None' is used to denote
545
+ no restrictions on a maximum or minimum.
546
+
547
+ :param new_value: Can be either 'None', a positive integer, which will set
548
+ the maximum or an integer 2-tuple of the format '(min, max)'.
549
+ """
550
+ invalid_input = False
551
+ exc_msg = "Can only assign positive single int or int-tuples of the format '(min, max)'"
552
+
553
+ # Empty values reset the cardinality to None.
554
+ if not new_value or new_value == (None , None ):
555
+ self ._val_cardinality = None
556
+
557
+ # Providing a single integer sets the maximum value in a tuple.
558
+ elif isinstance (new_value , int ) and new_value > 0 :
559
+ self ._val_cardinality = (None , new_value )
560
+
561
+ # Only integer 2-tuples of the format '(min, max)' are supported to set the cardinality
562
+ elif isinstance (new_value , tuple ) and len (new_value ) == 2 :
563
+ v_min = new_value [0 ]
564
+ v_max = new_value [1 ]
565
+
566
+ min_int = isinstance (v_min , int ) and v_min >= 0
567
+ max_int = isinstance (v_max , int ) and v_max >= 0
568
+
569
+ if max_int and min_int and v_max > v_min :
570
+ self ._val_cardinality = (v_min , v_max )
571
+
572
+ elif max_int and not v_min :
573
+ self ._val_cardinality = (None , v_max )
574
+
575
+ elif min_int and not v_max :
576
+ self ._val_cardinality = (v_min , None )
577
+
578
+ else :
579
+ invalid_input = True
580
+
581
+ # Use helpful exception message in the following case:
582
+ if max_int and min_int and v_max < v_min :
583
+ exc_msg = "Minimum larger than maximum (min=%s, max=%s)" % (v_min , v_max )
584
+ else :
585
+ invalid_input = True
586
+
587
+ if not invalid_input :
588
+ # Validate and inform user if the current values cardinality is violated
589
+ valid = validation .Validation (self )
590
+ for err in valid .errors :
591
+ print ("%s: %s" % (err .rank .capitalize (), err .msg ))
592
+ else :
593
+ raise ValueError (exc_msg )
594
+
595
+ def set_values_cardinality (self , min_val = None , max_val = None ):
596
+ """
597
+ Sets the values cardinality of a Property.
598
+
599
+ :param min_val: Required minimal number of values elements. None denotes
600
+ no restrictions on values elements minimum. Default is None.
601
+ :param max_val: Allowed maximal number of values elements. None denotes
602
+ no restrictions on values elements maximum. Default is None.
603
+ """
604
+ self .val_cardinality = (min_val , max_val )
605
+
510
606
def remove (self , value ):
511
607
"""
512
608
Remove a value from this property. Only the first encountered
0 commit comments