Skip to content

Commit 024da73

Browse files
committed
Fixed #543 - Add numeric value support for 'attribute' field
Signed-off-by: Chin Yeung Li <[email protected]>
1 parent 55abd3f commit 024da73

File tree

4 files changed

+137
-3
lines changed

4 files changed

+137
-3
lines changed

docs/source/specification.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,18 @@ Optional Boolean flag fields
352352

353353
- redistribute: Set this flag to yes if the component license requires source code
354354
redistribution. Defaults to no when absent.
355-
- attribute: Set this flag to yes if the component license requires publishing an attribution
356-
or credit notice. Defaults to no when absent.
357355
- track_changes: Set this flag to yes if the component license requires tracking changes made to
358356
a the component. Defaults to no when absent.
359357
- modified: Set this flag to yes if the component has been modified. Defaults to no when absent.
360358
- internal_use_only: Set this flag to yes if the component is used internal only.
361359
Defaults to no when absent.
362360

361+
Optional Boolean and Numberic fields
362+
------------------------------------
363+
364+
- attribute: This field can be either in boolean value: ('yes', 'y', 'true',
365+
'x', 'no', 'n', 'false') or numeric value field. Defaults to no when absent.
366+
363367
Optional Extension fields
364368
-------------------------
365369

src/attributecode/model.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,108 @@ def __eq__(self, other):
720720
and self.value == other.value)
721721

722722

723+
class BooleanAndNumbericField(SingleLineField):
724+
"""
725+
Field with either a boolean value or a numeric value. Validated value is
726+
False, True, None or numeric value.
727+
"""
728+
729+
def default_value(self):
730+
return None
731+
732+
true_flags = ('yes', 'y', 'true', 'x')
733+
false_flags = ('no', 'n', 'false')
734+
flag_values = true_flags + false_flags
735+
736+
def _validate(self, *args, **kwargs):
737+
"""
738+
Check that flag are valid with either boolean value or numeric value. Default flag to
739+
False. Return a list of errors.
740+
"""
741+
errors = super(BooleanAndNumbericField,
742+
self)._validate(*args, ** kwargs)
743+
self.about_file_path = kwargs.get('about_file_path')
744+
flag = self.get_value(self.original_value)
745+
if flag is False:
746+
name = self.name
747+
val = self.original_value
748+
about_file_path = self.about_file_path
749+
flag_values = self.flag_values
750+
msg = (u'Path: %(about_file_path)s - Field %(name)s: Invalid value: %(val)r is not '
751+
u'one of: %(flag_values)s and it is not a numeric value.' % locals())
752+
errors.append(Error(ERROR, msg))
753+
self.value = None
754+
elif flag is None:
755+
name = self.name
756+
msg = (u'Field %(name)s: field is present but empty. ' % locals())
757+
errors.append(Error(INFO, msg))
758+
self.value = None
759+
else:
760+
if flag == u'yes' or flag is True:
761+
self.value = True
762+
elif flag == u'no':
763+
self.value = False
764+
else:
765+
self.value = flag
766+
return errors
767+
768+
def get_value(self, value):
769+
"""
770+
Return a normalized existing value if found in the list of
771+
possible values or None if empty or False if not found or original value
772+
if it is not a boolean value
773+
"""
774+
if value is None or value == '':
775+
return None
776+
777+
if isinstance(value, bool):
778+
return value
779+
else:
780+
if isinstance(value, str):
781+
value = value.strip()
782+
if not value:
783+
return None
784+
785+
value = value.lower()
786+
if value in self.flag_values:
787+
if value in self.true_flags:
788+
return u'yes'
789+
else:
790+
return u'no'
791+
else:
792+
if value.isdigit():
793+
return value
794+
else:
795+
return False
796+
elif isinstance(value, int):
797+
return value
798+
else:
799+
return False
800+
801+
@property
802+
def has_content(self):
803+
"""
804+
Return true if it has content regardless of what value, False otherwise
805+
"""
806+
if self.original_value:
807+
return True
808+
return False
809+
810+
def _serialized_value(self):
811+
# default normalized values for serialization
812+
if self.value:
813+
if isinstance(self.value, bool):
814+
return u'yes'
815+
else:
816+
return self.value
817+
elif self.value is False:
818+
return u'no'
819+
else:
820+
# self.value is None
821+
# TODO: should we serialize to No for None???
822+
return u''
823+
824+
723825
def validate_fields(fields, about_file_path, running_inventory, base_dir,
724826
reference_dir=None):
725827
"""
@@ -810,7 +912,7 @@ def set_standard_fields(self):
810912
('notice_url', UrlField()),
811913

812914
('redistribute', BooleanField()),
813-
('attribute', BooleanField()),
915+
('attribute', BooleanAndNumbericField()),
814916
('track_changes', BooleanField()),
815917
('modified', BooleanField()),
816918
('internal_use_only', BooleanField()),

tests/test_model.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,27 @@ def test_About_boolean_value(self):
648648
assert a.redistribute.value is True
649649
assert a.track_changes.value is None
650650

651+
def test_About_boolean_numeric_value(self):
652+
test_file = get_test_loc('test_model/parse/boolean_numeric_data.about')
653+
a = model.About(test_file)
654+
expected_msg = "Field track_changes is present but empty."
655+
assert expected_msg in a.errors[0].message
656+
# Context of the test file
657+
"""
658+
about_resource: .
659+
name: boolean_data
660+
attribute: 3
661+
modified: true
662+
internal_use_only: no
663+
redistribute: yes
664+
track_changes:
665+
"""
666+
assert a.attribute.value == '3'
667+
assert a.modified.value is True
668+
assert a.internal_use_only.value is False
669+
assert a.redistribute.value is True
670+
assert a.track_changes.value is None
671+
651672
def test_About_contains_about_file_path(self):
652673
test_file = get_test_loc('test_model/serialize/about.ABOUT')
653674
# TODO: I am not sure this override of the about_file_path makes sense
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
about_resource: .
2+
name: boolean_data
3+
attribute: 3
4+
modified: true
5+
internal_use_only: no
6+
redistribute: yes
7+
track_changes:

0 commit comments

Comments
 (0)