Skip to content

Commit a231956

Browse files
authored
Merge pull request #398 from fschrader1992/validation
Property Reorder & Value Insert
2 parents 66c97fc + 2316199 commit a231956

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed

odml/property.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,32 @@ def get_terminology_equivalent(self):
772772
except KeyError:
773773
return None
774774

775+
def _reorder(self, childlist, new_index):
776+
lst = childlist
777+
old_index = lst.index(self)
778+
779+
# 2 cases: insert after old_index / insert before
780+
if new_index > old_index:
781+
new_index += 1
782+
lst.insert(new_index, self)
783+
if new_index < old_index:
784+
del lst[old_index + 1]
785+
else:
786+
del lst[old_index]
787+
return old_index
788+
789+
def reorder(self, new_index):
790+
"""
791+
Move this object in its parent child-list to the position *new_index*.
792+
793+
:return: The old index at which the object was found.
794+
"""
795+
if not self.parent:
796+
raise ValueError("odml.Property.reorder: "
797+
"Property has no parent, cannot reorder in parent list.")
798+
799+
return self._reorder(self.parent.properties, new_index)
800+
775801
def extend(self, obj, strict=True):
776802
"""
777803
Extend the list of values stored in this property by the passed values. Method
@@ -857,6 +883,58 @@ def append(self, obj, strict=True):
857883

858884
self._values.append(dtypes.get(new_value[0], self.dtype))
859885

886+
def insert(self, index, obj, strict=True):
887+
"""
888+
Insert a single value to the list of stored values. Method will raise
889+
a ValueError if the passed value cannot be converted to the current dtype.
890+
891+
:param obj: the additional value.
892+
:param index: position of the new value
893+
:param strict: a Bool that controls whether dtypes must match. Default is True.
894+
"""
895+
896+
# Ignore empty values before nasty stuff happens, but make sure
897+
# 0 and False get through.
898+
if obj in [None, "", [], {}]:
899+
return
900+
901+
if not self.values:
902+
self.values = obj
903+
return
904+
905+
new_value = self._convert_value_input(obj)
906+
if len(new_value) > 1:
907+
raise ValueError("odml.property.insert: Use extend to add a list of values!")
908+
909+
new_value = self._convert_value_input(obj)
910+
if len(new_value) > 1:
911+
raise ValueError("odml.property.insert: Use extend to add a list of values!")
912+
913+
if self._dtype.endswith("-tuple"):
914+
t_count = int(self._dtype.split("-")[0])
915+
new_value = odml_tuple_import(t_count, new_value)
916+
917+
if len(new_value) > 0 and strict and \
918+
dtypes.infer_dtype(new_value[0]) != self.dtype:
919+
920+
type_check = dtypes.infer_dtype(new_value[0])
921+
if not (type_check == "string" and self.dtype in dtypes.special_dtypes) \
922+
and not self.dtype.endswith("-tuple"):
923+
msg = "odml.Property.insert: passed value data type found "
924+
msg += "(\"%s\") does not match expected dtype \"%s\"!" % (type_check,
925+
self._dtype)
926+
raise ValueError(msg)
927+
928+
if not self._validate_values(new_value):
929+
raise ValueError("odml.Property.insert: passed value(s) cannot be converted "
930+
"to data type \'%s\'!" % self._dtype)
931+
932+
if index > len(self._values):
933+
warnings.warn("odml.Property.insert: Index %i larger than length of property.values. "
934+
"Added value at end of list." % index, stacklevel=2)
935+
936+
self._values.insert(index, dtypes.get(new_value[0], self.dtype))
937+
860938
def pprint(self, indent=2, max_length=80, current_depth=-1):
861939
"""
862940
Pretty print method to visualize Properties and Section-Property trees.

test/test_property.py

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
import datetime
23

34
from odml import Property, Section, Document, DType
45
from odml.property import BaseProperty
@@ -245,6 +246,45 @@ def test_value_append(self):
245246
self.assertEqual(len(prop9), 3)
246247
self.assertRaises(ValueError, prop9.append, [[10, 11]])
247248

249+
prop10 = Property(name="prop10", dtype="date", values=['2011-12-01', '2011-12-02'])
250+
with self.assertRaises(ValueError):
251+
prop10.append('2011-12-03', strict=True)
252+
self.assertEqual(prop10.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2)])
253+
prop10.append('2011-12-03', strict=False)
254+
self.assertEqual(prop10.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
255+
datetime.date(2011, 12, 3)])
256+
prop10.append([datetime.date(2011, 12, 4)], strict=True)
257+
self.assertEqual(prop10.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
258+
datetime.date(2011, 12, 3), datetime.date(2011, 12, 4)])
259+
260+
261+
prop11 = Property(name="prop11", dtype="time", values=['12:00:01', '12:00:02'])
262+
with self.assertRaises(ValueError):
263+
prop11.append('12:00:03', strict=True)
264+
self.assertEqual(prop11.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2)])
265+
prop11.append('12:00:03', strict=False)
266+
self.assertEqual(prop11.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
267+
datetime.time(12, 0, 3)])
268+
prop11.append([datetime.time(12, 0, 4)], strict=True)
269+
self.assertEqual(prop11.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
270+
datetime.time(12, 0, 3), datetime.time(12, 0, 4)])
271+
272+
prop12 = Property(name="prop12", dtype="datetime",
273+
values=['2011-12-01 12:00:01', '2011-12-01 12:00:02'])
274+
with self.assertRaises(ValueError):
275+
prop12.append('2011-12-01 12:00:03', strict=True)
276+
self.assertEqual(prop12.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
277+
datetime.datetime(2011, 12, 1, 12, 0, 2)])
278+
prop12.append('2011-12-01 12:00:03', strict=False)
279+
self.assertEqual(prop12.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
280+
datetime.datetime(2011, 12, 1, 12, 0, 2),
281+
datetime.datetime(2011, 12, 1, 12, 0, 3)])
282+
prop12.append([datetime.datetime(2011, 12, 1, 12, 0, 4)], strict=True)
283+
self.assertEqual(prop12.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
284+
datetime.datetime(2011, 12, 1, 12, 0, 2),
285+
datetime.datetime(2011, 12, 1, 12, 0, 3),
286+
datetime.datetime(2011, 12, 1, 12, 0, 4)])
287+
248288
def test_value_extend(self):
249289
prop = Property(name="extend")
250290

@@ -353,6 +393,215 @@ def test_value_extend(self):
353393
self.assertEqual(len(prop4), 5)
354394
self.assertRaises(ValueError, prop4.extend, [[10, 11]])
355395

396+
prop4 = Property(name="prop4", dtype="date", values=['2011-12-01', '2011-12-02'])
397+
with self.assertRaises(ValueError):
398+
prop4.extend('2011-12-03', strict=True)
399+
self.assertEqual(prop4.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2)])
400+
prop4.extend('2011-12-03', strict=False)
401+
self.assertEqual(prop4.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
402+
datetime.date(2011, 12, 3)])
403+
prop4.extend([datetime.date(2011, 12, 4)], strict=True)
404+
self.assertEqual(prop4.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
405+
datetime.date(2011, 12, 3), datetime.date(2011, 12, 4)])
406+
prop4.extend([datetime.date(2011, 12, 5), datetime.date(2011, 12, 6)], strict=True)
407+
self.assertEqual(prop4.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
408+
datetime.date(2011, 12, 3), datetime.date(2011, 12, 4),
409+
datetime.date(2011, 12, 5), datetime.date(2011, 12, 6)])
410+
with self.assertRaises(ValueError):
411+
prop4.extend(['2011-12-03', 'abc'], strict=False)
412+
413+
414+
prop5 = Property(name="prop5", dtype="time", values=['12:00:01', '12:00:02'])
415+
with self.assertRaises(ValueError):
416+
prop5.extend('12:00:03', strict=True)
417+
self.assertEqual(prop5.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2)])
418+
prop5.extend('12:00:03', strict=False)
419+
self.assertEqual(prop5.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
420+
datetime.time(12, 0, 3)])
421+
prop5.extend([datetime.time(12, 0, 4)], strict=True)
422+
self.assertEqual(prop5.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
423+
datetime.time(12, 0, 3), datetime.time(12, 0, 4)])
424+
prop5.extend([datetime.time(12, 0, 5), datetime.time(12, 0, 6)], strict=True)
425+
self.assertEqual(prop5.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
426+
datetime.time(12, 0, 3), datetime.time(12, 0, 4),
427+
datetime.time(12, 0, 5), datetime.time(12, 0, 6)])
428+
with self.assertRaises(ValueError):
429+
prop4.extend(['12:00:07', 'abc'], strict=False)
430+
431+
prop6 = Property(name="prop6", dtype="datetime",
432+
values=['2011-12-01 12:00:01', '2011-12-01 12:00:02'])
433+
with self.assertRaises(ValueError):
434+
prop6.extend('2011-12-01 12:00:03', strict=True)
435+
self.assertEqual(prop6.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
436+
datetime.datetime(2011, 12, 1, 12, 0, 2)])
437+
prop6.extend('2011-12-01 12:00:03', strict=False)
438+
self.assertEqual(prop6.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
439+
datetime.datetime(2011, 12, 1, 12, 0, 2),
440+
datetime.datetime(2011, 12, 1, 12, 0, 3)])
441+
prop6.extend([datetime.datetime(2011, 12, 1, 12, 0, 4)], strict=True)
442+
self.assertEqual(prop6.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
443+
datetime.datetime(2011, 12, 1, 12, 0, 2),
444+
datetime.datetime(2011, 12, 1, 12, 0, 3),
445+
datetime.datetime(2011, 12, 1, 12, 0, 4)])
446+
prop6.extend([datetime.datetime(2011, 12, 1, 12, 0, 5),
447+
datetime.datetime(2011, 12, 1, 12, 0, 6)], strict=True)
448+
self.assertEqual(prop6.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
449+
datetime.datetime(2011, 12, 1, 12, 0, 2),
450+
datetime.datetime(2011, 12, 1, 12, 0, 3),
451+
datetime.datetime(2011, 12, 1, 12, 0, 4),
452+
datetime.datetime(2011, 12, 1, 12, 0, 5),
453+
datetime.datetime(2011, 12, 1, 12, 0, 6)])
454+
with self.assertRaises(ValueError):
455+
prop4.extend(['2011-12-03 12:00:07', 'abc'], strict=False)
456+
457+
def test_insert(self):
458+
prop1 = Property(name="prop1", dtype="int", values=[0,2])
459+
prop1.insert(1, 1)
460+
self.assertEqual(prop1.values, [0, 1, 2])
461+
prop1.insert(4, 3)
462+
self.assertEqual(prop1.values, [0, 1, 2, 3])
463+
464+
with self.assertRaises(ValueError):
465+
prop1.append([4, 5])
466+
self.assertEqual(prop1.values, [0, 1, 2, 3])
467+
468+
with self.assertRaises(ValueError):
469+
prop1.insert(1, 3.14)
470+
with self.assertRaises(ValueError):
471+
prop1.insert(1, True)
472+
with self.assertRaises(ValueError):
473+
prop1.insert(1, "5.927")
474+
self.assertEqual(prop1.values, [0, 1, 2, 3])
475+
476+
prop2 = Property(name="prop2", dtype="int", values=[1, 2])
477+
prop2.insert(1, 3.14, strict=False)
478+
self.assertEqual(prop2.values, [1, 3, 2])
479+
prop2.insert(1, True, strict=False)
480+
self.assertEqual(prop2.values, [1, 1, 3, 2])
481+
prop2.insert(1, "5.927", strict=False)
482+
self.assertEqual(prop2.values, [1, 5, 1, 3, 2])
483+
484+
prop3 = Property(name="prop3", dtype="string", values=["a", "c"])
485+
prop3.insert(1, "b")
486+
self.assertEqual(prop3.values, ["a", "b", "c"])
487+
prop3.insert(1, 1, strict=False)
488+
self.assertEqual(prop3.values, ["a", "1", "b", "c"])
489+
with self.assertRaises(ValueError):
490+
prop3.insert(1, 2, strict=True)
491+
self.assertEqual(prop3.values, ["a", "1", "b", "c"])
492+
493+
prop4 = Property(name="prop4", dtype="float", values=[1.1, 1.3])
494+
prop4.insert(1, 1.2)
495+
self.assertEqual(prop4.values, [1.1, 1.2, 1.3])
496+
prop4.insert(1, 2, strict=False)
497+
self.assertEqual(prop4.values, [1.1, 2.0, 1.2, 1.3])
498+
with self.assertRaises(ValueError):
499+
prop4.insert(1, 2, strict=True)
500+
self.assertEqual(prop4.values, [1.1, 2.0, 1.2, 1.3])
501+
502+
prop5 = Property(name="prop5", dtype="2-tuple", values=["(1; 2)", "(5; 6)"])
503+
prop5.insert(1, "(3; 4)", strict=True)
504+
self.assertEqual(prop5.values, [['1', '2'], ['3', '4'], ['5', '6']])
505+
prop5.insert(1, [['4', '5']], strict=True)
506+
self.assertEqual(prop5.values, [['1', '2'], ['4', '5'], ['3', '4'], ['5', '6']])
507+
508+
prop6 = Property(name="prop6", dtype="boolean", values=[True, True])
509+
prop6.insert(1, False)
510+
self.assertEqual(prop6.values, [True, False, True])
511+
prop6.insert(1, 1, strict=False)
512+
self.assertEqual(prop6.values, [True, True, False, True])
513+
with self.assertRaises(ValueError):
514+
prop6.insert(1, 2, strict=False)
515+
self.assertEqual(prop6.values, [True, True, False, True])
516+
with self.assertRaises(ValueError):
517+
prop6.insert(1, 0, strict=True)
518+
self.assertEqual(prop6.values, [True, True, False, True])
519+
520+
prop7 = Property(name="prop7", dtype="date", values=['2011-12-01', '2011-12-04'])
521+
with self.assertRaises(ValueError):
522+
prop7.insert(1, '2011-12-03', strict=True)
523+
self.assertEqual(prop7.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 4)])
524+
prop7.insert(1, '2011-12-03', strict=False)
525+
self.assertEqual(prop7.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 3),
526+
datetime.date(2011, 12, 4)])
527+
prop7.insert(1, [datetime.date(2011, 12, 2)], strict=True)
528+
self.assertEqual(prop7.values, [datetime.date(2011, 12, 1), datetime.date(2011, 12, 2),
529+
datetime.date(2011, 12, 3), datetime.date(2011, 12, 4)])
530+
531+
532+
prop8 = Property(name="prop8", dtype="time", values=['12:00:01', '12:00:04'])
533+
with self.assertRaises(ValueError):
534+
prop8.insert(1, '12:00:03', strict=True)
535+
self.assertEqual(prop8.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 4)])
536+
prop8.insert(1, '12:00:03', strict=False)
537+
self.assertEqual(prop8.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 3),
538+
datetime.time(12, 0, 4)])
539+
prop8.insert(1, [datetime.time(12, 0, 2)], strict=True)
540+
self.assertEqual(prop8.values, [datetime.time(12, 0, 1), datetime.time(12, 0, 2),
541+
datetime.time(12, 0, 3), datetime.time(12, 0, 4)])
542+
543+
prop9 = Property(name="prop9", dtype="datetime",
544+
values=['2011-12-01 12:00:01', '2011-12-01 12:00:04'])
545+
with self.assertRaises(ValueError):
546+
prop9.insert(1, '2011-12-01 12:00:03', strict=True)
547+
self.assertEqual(prop9.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
548+
datetime.datetime(2011, 12, 1, 12, 0, 4)])
549+
prop9.insert(1, '2011-12-01 12:00:03', strict=False)
550+
self.assertEqual(prop9.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
551+
datetime.datetime(2011, 12, 1, 12, 0, 3),
552+
datetime.datetime(2011, 12, 1, 12, 0, 4)])
553+
prop9.insert(1, [datetime.datetime(2011, 12, 1, 12, 0, 2)], strict=True)
554+
self.assertEqual(prop9.values, [datetime.datetime(2011, 12, 1, 12, 0, 1),
555+
datetime.datetime(2011, 12, 1, 12, 0, 2),
556+
datetime.datetime(2011, 12, 1, 12, 0, 3),
557+
datetime.datetime(2011, 12, 1, 12, 0, 4)])
558+
559+
prop10 = Property(name="prop", value=["Earth is\n No. 3."], dtype=DType.text)
560+
prop10.insert(1, "Mars is\n No. 4.", strict=False)
561+
self.assertEqual(len(prop10), 2)
562+
self.assertEqual(prop10.values, ["Earth is\n No. 3.", "Mars is\n No. 4."])
563+
prop10.insert(1, 'A new world emerged?', strict=True)
564+
self.assertEqual(prop10.values, ["Earth is\n No. 3.",
565+
"A new world emerged?",
566+
"Mars is\n No. 4."])
567+
prop10.insert(1, 1, strict=False)
568+
self.assertEqual(prop10.values, ["Earth is\n No. 3.", "1",
569+
"A new world emerged?",
570+
"Mars is\n No. 4."])
571+
with self.assertRaises(ValueError):
572+
prop10.insert(1, 1, strict=True)
573+
self.assertEqual(prop10.values, ["Earth is\n No. 3.", "1",
574+
"A new world emerged?",
575+
"Mars is\n No. 4."])
576+
577+
def test_reorder(self):
578+
sec = Section()
579+
prop_zero = Property(name="prop_zero", parent=sec)
580+
prop_one = Property(name="prop_one", parent=sec)
581+
prop_two = Property(name="prop_two", parent=sec)
582+
prop_three = Property(name="prop_three", parent=sec)
583+
584+
self.assertEqual(sec.properties[0].name, prop_zero.name)
585+
self.assertEqual(sec.properties[2].name, prop_two.name)
586+
prop_two.reorder(0)
587+
588+
self.assertEqual(sec.properties[0].name, prop_two.name)
589+
self.assertEqual(sec.properties[1].name, prop_zero.name)
590+
self.assertEqual(sec.properties[2].name, prop_one.name)
591+
self.assertEqual(sec.properties[3].name, prop_three.name)
592+
593+
prop_two.reorder(2)
594+
595+
self.assertEqual(sec.properties[0].name, prop_zero.name)
596+
self.assertEqual(sec.properties[1].name, prop_one.name)
597+
self.assertEqual(sec.properties[2].name, prop_two.name)
598+
self.assertEqual(sec.properties[3].name, prop_three.name)
599+
600+
# Test Exception on unconnected property
601+
prop = Property(name="main")
602+
with self.assertRaises(ValueError):
603+
prop.reorder(0)
604+
356605
def test_get_set_value(self):
357606
values = [1, 2, 3, 4, 5]
358607
prop = Property("property", value=values)

0 commit comments

Comments
 (0)