diff --git a/CHANGELOG.md b/CHANGELOG.md index e00c0a51e95dfc24373ccc08817206d279322f16..0ca5f692e8c9ca1a89d2b48a23ba4ad017711234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Introduce a cfood that can create a Record structure based on the contents of a hdf5 file h5py is now an optional dependency - table importer implementations for csv and tsv +- string-in-list check for table imports ### Changed ### diff --git a/integrationtests/model.yml b/integrationtests/model.yml index ab302f089de8ddc1782c075ad8ee26ea1259fee2..e1753a9f84d6eff7a9a59c5d17ca4f9f9da7c4c6 100644 --- a/integrationtests/model.yml +++ b/integrationtests/model.yml @@ -9,6 +9,7 @@ Experiment: # TODO empty recommended_properties is a problem #recommended_properties: responsible: + datatype: LIST<Person> Project: SoftwareVersion: recommended_properties: @@ -30,8 +31,6 @@ Person: email: datatype: TEXT description: 'Email of a Person.' -responsible: - datatype: REFERENCE revisionOf: datatype: REFERENCE results: diff --git a/src/caosadvancedtools/cfoods/h5.py b/src/caosadvancedtools/cfoods/h5.py index 9defe77115db7687d3a6c5f27bf7f3d268e605fc..6c68edd3668fec957126aa3234a830aab98fcd25 100644 --- a/src/caosadvancedtools/cfoods/h5.py +++ b/src/caosadvancedtools/cfoods/h5.py @@ -6,7 +6,7 @@ # Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com> # Copyright (C) 2021 Henrik tom Wörden <h.tomwoerden@indiscale.com> # Copyright (C) 2021 Alexander Kreft -# Copyright (C) 2021 Laboratory for Fluid Physics and Biocomplexity, +# Copyright (C) 2021 Laboratory for Fluid Physics and Biocomplexity, # Max-Planck-Insitute für Dynamik und Selbstorganisation <www.lfpn.ds.mpg.de> # # This program is free software: you can redistribute it and/or modify diff --git a/src/caosadvancedtools/models/parser.py b/src/caosadvancedtools/models/parser.py index 1eda090757336506436621f9f430cf852a8a2348..e56a492fa3e9199a312d374a622770e7836f42cb 100644 --- a/src/caosadvancedtools/models/parser.py +++ b/src/caosadvancedtools/models/parser.py @@ -252,9 +252,8 @@ class Parser(object): # is it a property and "datatype" in definition # but not simply an RT of the model - and not (definition["datatype"] in self.model - or _get_listdatatype( - definition["datatype"]) in self.model)): + and not (_get_listdatatype(definition["datatype"]) == name and + _get_listdatatype(definition["datatype"]) in self.model)): # and create the new property self.model[name] = db.Property(name=name, diff --git a/src/caosadvancedtools/table_importer.py b/src/caosadvancedtools/table_importer.py index 6b86b1098fd58c784ba5a190174aced3c4323281..cb61e8389de69a2d0d0527ad01cb8b9991b19ece 100755 --- a/src/caosadvancedtools/table_importer.py +++ b/src/caosadvancedtools/table_importer.py @@ -88,7 +88,10 @@ def date_converter(val, fmt="%Y-%m-%d"): converts it using format string """ - return datetime_converter(val, fmt=fmt).date() + if val is None: + return None + else: + return datetime_converter(val, fmt=fmt).date() def incomplete_date_converter(val, fmts={"%Y-%m-%d": "%Y-%m-%d", @@ -145,6 +148,43 @@ def win_path_converter(val): return path.as_posix() +def string_in_list(val, options, ignore_case=True): + """Return the given value if it is contained in options, raise an + error otherwise. + + Parameters + ---------- + val : str + String value to be checked. + options : list<str> + List of possible values that val may obtain + ignore_case : bool, optional + Specify whether the comparison of val and the possible options + should ignor capitalization. Default is True. + + Returns + ------- + val : str + The original value if it is contained in options + + Raises + ------ + ValueError + If val is not contained in options. + """ + if ignore_case: + val = val.lower() + options = [o.lower() for o in options] + + if val not in options: + raise ValueError( + "Field value is '{}', but it should be one of the following " + "values: {}.".format(val, ", ".join( + ["'{}'".format(o) for o in options]))) + + return val + + class TableImporter(object): def __init__(self, converters, obligatory_columns=None, unique_keys=None): """ diff --git a/unittests/test_parser.py b/unittests/test_parser.py index 44b01a3ab2af741497e68cb7a3b912f433d6fe35..161e2873a9c01f9ce415818116b9e4cf9aeadb5c 100644 --- a/unittests/test_parser.py +++ b/unittests/test_parser.py @@ -274,6 +274,22 @@ A: parse_model_from_string(yaml) self.assertIn("line 3", yde.exception.args[0]) + def test_reference_property(self): + """Test correct creation of reference property using an RT.""" + modeldef = """A: + recommended_properties: + ref: + datatype: LIST<A> +""" + model = parse_model_from_string(modeldef) + self.assertEqual(len(model), 2) + for key in model.keys(): + if key == "A": + self.assertTrue(isinstance(model[key], db.RecordType)) + elif key == "ref": + self.assertTrue(isinstance(model[key], db.Property)) + self.assertEqual(model[key].datatype, "LIST<A>") + class ExternTest(unittest.TestCase): """TODO Testing the "extern" keyword in the YAML.""" diff --git a/unittests/test_table_importer.py b/unittests/test_table_importer.py index b36f8bf9eeda9166fb8ab0d0f1b43d5d1c5276a4..b574c867881141928ac59c2b002fb7f185dac7bb 100644 --- a/unittests/test_table_importer.py +++ b/unittests/test_table_importer.py @@ -36,6 +36,7 @@ from caosadvancedtools.table_importer import (XLSImporter, assure_name_format, incomplete_date_converter, win_path_converter, win_path_list_converter, + string_in_list, yes_no_converter) @@ -52,6 +53,16 @@ class ConverterTest(unittest.TestCase): self.assertRaises(ValueError, yes_no_converter, "True") self.assertRaises(ValueError, yes_no_converter, "true") + def test_string_in_list(self): + self.assertEqual("false", string_in_list("false", + ["FALSE", "TRUE"])) + self.assertEqual("FALSE", string_in_list("FALSE", + ["FALSE", "TRUE"], False)) + self.assertRaises(ValueError, string_in_list, "FALSE", []) + self.assertRaises(ValueError, string_in_list, "FALSE", ["fals"]) + self.assertRaises(ValueError, string_in_list, + "FALSE", ["false"], False) + def test_assure_name_format(self): self.assertEqual(assure_name_format("Müstermann, Max"), "Müstermann, Max") @@ -65,7 +76,7 @@ class ConverterTest(unittest.TestCase): ["/this/computer"]) self.assertEqual(win_path_list_converter( r"\this\computer,\this\computer"), - ["/this/computer", "/this/computer"]) + ["/this/computer", "/this/computer"]) @pytest.mark.xfail(reason="To be fixed, see Issue #34") def test_datetime(self):